#include <sdk.h>
#include <wx/frame.h>
#include <wx/gauge.h>
#include <wx/listctrl.h>
#include <wx/xrc/xmlres.h>
#include <wx/sizer.h>
#include <wx/button.h>
#include <wx/stattext.h>
#include <wx/statline.h>
#include <wx/ffile.h>
#include <wx/utils.h>
#include <wx/uri.h>
#include <wx/xml/xml.h>
#include <prep.h>
#include <manager.h>
#include <sdk_events.h>
#include <pipedprocess.h>
#include <configmanager.h>
#include <compilercommandgenerator.h>
#include <debuggermanager.h>
#include <incremental_select_helper.h>
#include <logmanager.h>
#include <macrosmanager.h>
#include <projectmanager.h>
#include <editormanager.h>
#include <scriptingmanager.h>
#include <configurationpanel.h>
#include <pluginmanager.h>
#include <M_Code_editor.h>
#include <filefilters.h>
#include <infowindow.h>
#include "compilergcc.h"
#include "compileroptionsdlg.h"
#include "directcommands.h"
#include "globals.h"
#include "M_Code_StyledTextCtrl.h"
#include "compilerC.h"
#include "compilerXML.h"
#include <scripting/bindings/sc_base_types.h>

const int maxTargetInMenus = 40;
const int idBuildLog = wxNewId();

namespace ScriptBindings {
  static int gBuildLogId = -1;
  void gBuildLog( const wxString& msg ) {
    Manager::Get()->GetLogManager()->Log( msg, gBuildLogId );
  }
}

class BuildLogger : public TextCtrlLogger {
    wxPanel* panel;
    wxBoxSizer* sizer;
  public:
    wxGauge* progress;
    BuildLogger() : TextCtrlLogger( true ), panel( 0 ), sizer( 0 ), progress( 0 ) {}
    void UpdateSettings() {
      TextCtrlLogger::UpdateSettings();
      style[caption].SetAlignment( wxTEXT_ALIGNMENT_DEFAULT );
      style[caption].SetFont( style[error].GetFont() );
      style[error].SetFont( style[info].GetFont() );
    }
    virtual wxWindow* CreateControl( wxWindow* parent ) {
      panel = new wxPanel( parent );
      TextCtrlLogger::CreateControl( panel );
      control->SetId( idBuildLog );
      sizer = new wxBoxSizer( wxVERTICAL );
      sizer->Add( control, 1, wxEXPAND, 0 );
      panel->SetSizer( sizer );
      return panel;
    }
    void OpenLink( long urlStart, long urlEnd ) {
      if( !control ) {
        return;
      }
      wxString url = control->GetRange( urlStart, urlEnd );
      if( false && url.StartsWith( _T( "file://" ) ) ) {
        url.Remove( 0, 7 );
      }
      cbMimePlugin* p = Manager::Get()->GetPluginManager()->GetMIMEHandlerForFile( url );
      if( p ) {
        p->OpenFile( url );
      } else
      { wxLaunchDefaultBrowser( url ); }
    }
};

namespace {
  PluginRegistrant<CompilerGCC> reg( _T( "Compiler" ) );
  static const wxString strCONSOLE_RUNNER( _T( "console_runner" ) );
  static const wxString strSLASH( _T( "/" ) );
  static const wxString strSPACE( _T( " " ) );
  static const wxString strQUOTE( _T( "'" ) );
}

int idTimerPollCompiler = XRCID( "idTimerPollCompiler" );
int idMenuCompile = XRCID( "idCompilerMenuCompile" );
int idMenuCompileTarget = wxNewId();
int idMenuCompileFromProjectManager = wxNewId();
int idMenuProjectCompilerOptions = wxNewId();
int idMenuProjectCompilerOptionsFromProjectManager = wxNewId();
int idMenuTargetCompilerOptions = wxNewId();
int idMenuTargetCompilerOptionsSub = wxNewId();
int idMenuCompileFile = XRCID( "idCompilerMenuCompileFile" );
int idMenuCompileFileFromProjectManager = wxNewId();
int idMenuCleanFileFromProjectManager = wxNewId();
int idMenuRebuild = XRCID( "idCompilerMenuRebuild" );
int idMenuRebuildTarget = wxNewId();
int idMenuRebuildFromProjectManager = wxNewId();
int idMenuClean = XRCID( "idCompilerMenuClean" );
int idMenuBuildWorkspace = XRCID( "idCompilerMenuBuildWorkspace" );
int idMenuRebuildWorkspace = XRCID( "idCompilerMenuRebuildWorkspace" );
int idMenuCleanWorkspace = XRCID( "idCompilerMenuCleanWorkspace" );
int idMenuCleanTarget = wxNewId();
int idMenuCleanFromProjectManager = wxNewId();
int idMenuCompileAndRun = XRCID( "idCompilerMenuCompileAndRun" );
int idMenuRun = XRCID( "idCompilerMenuRun" );
int idMenuKillProcess = XRCID( "idCompilerMenuKillProcess" );
int idMenuSelectTarget = XRCID( "idCompilerMenuSelectTarget" );
int idMenuSelectTargetOther[maxTargetInMenus];
int idMenuSelectTargetDialog = XRCID( "idMenuSelectTargetDialog" );
int idMenuSelectTargetHasMore = wxNewId();
int idMenuNextError = XRCID( "idCompilerMenuNextError" );
int idMenuPreviousError = XRCID( "idCompilerMenuPreviousError" );
int idMenuClearErrors = XRCID( "idCompilerMenuClearErrors" );
int idMenuSettings = XRCID( "idCompilerMenuSettings" );
int idToolTarget = XRCID( "idToolTarget" );
int idGCCProcess = wxNewId();

BEGIN_EVENT_TABLE( CompilerGCC, cbCompilerPlugin )
  EVT_UPDATE_UI( idMenuCompile, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCompileTarget, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCompileFromProjectManager, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuProjectCompilerOptions, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuTargetCompilerOptions, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuTargetCompilerOptionsSub, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCompileFile, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCompileFileFromProjectManager, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCleanFileFromProjectManager, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuRebuild, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuRebuildTarget, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuRebuildFromProjectManager, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuBuildWorkspace, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuRebuildWorkspace, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuClean, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCleanWorkspace, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCleanTarget, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCleanFromProjectManager, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuCompileAndRun, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuRun, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuKillProcess, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuSelectTarget, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuNextError, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuPreviousError, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuClearErrors, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuSettings, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idToolTarget, CompilerGCC::OnUpdateUI )
  EVT_UPDATE_UI( idMenuSelectTargetDialog, CompilerGCC::OnUpdateUI )
  EVT_IDLE( CompilerGCC::OnIdle )
  EVT_TIMER( idTimerPollCompiler, CompilerGCC::OnTimer )
  EVT_MENU( idMenuRun, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCompileAndRun, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCompile, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCompileFromProjectManager, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCompileFile, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCompileFileFromProjectManager, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCleanFileFromProjectManager, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuRebuild, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuRebuildFromProjectManager, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuBuildWorkspace, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuRebuildWorkspace, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuProjectCompilerOptions, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuProjectCompilerOptionsFromProjectManager, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuTargetCompilerOptions, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuClean, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCleanWorkspace, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuCleanFromProjectManager, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuKillProcess, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuNextError, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuPreviousError, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuClearErrors, CompilerGCC::Dispatcher )
  EVT_MENU( idMenuSettings, CompilerGCC::Dispatcher )
  EVT_TEXT_URL( idBuildLog, CompilerGCC::TextURL )
  EVT_CHOICE( idToolTarget, CompilerGCC::OnSelectTarget )
  EVT_MENU( idMenuSelectTargetDialog, CompilerGCC::OnSelectTarget )
  EVT_PIPEDPROCESS_STDOUT( idGCCProcess, CompilerGCC::OnGCCOutput )
  EVT_PIPEDPROCESS_STDERR( idGCCProcess, CompilerGCC::OnGCCError )
  EVT_PIPEDPROCESS_TERMINATED( idGCCProcess, CompilerGCC::OnGCCTerminated )
END_EVENT_TABLE()

CompilerGCC::CompilerGCC() :
  m_RealTargetsStartIndex( 0 ),
  m_RealTargetIndex( 0 ),
  m_PageIndex( -1 ),
  m_ListPageIndex( -1 ),
  m_Menu( 0L ),
  m_TargetMenu( 0L ),
  m_TargetIndex( -1 ),
  m_pErrorsMenu( 0L ),
  m_pProject( 0L ),
  m_pTbar( 0L ),
  m_pLog( 0L ),
  m_pListLog( 0L ),
  m_pToolTarget( 0L ),
  m_RunAfterCompile( false ),
  m_LastExitCode( 0 ),
  m_NotifiedMaxErrors( false ),
  m_pBuildingProject( 0 ),
  m_BuildJob( bjIdle ),
  m_NextBuildState( bsNone ),
  m_pLastBuildingProject( 0 ),
  m_pLastBuildingTarget( 0 ),
  m_Clean( false ),
  m_Build( false ),
  m_LastBuildStep( true ),
  m_RunTargetPostBuild( false ),
  m_RunProjectPostBuild( false ),
  m_IsWorkspaceOperation( false ) {
  if( !Manager::LoadResource( _T( "Compiler.zip" ) ) ) {
    NotifyMissingFile( _T( "Compiler.zip" ) );
  }
}

CompilerGCC::~CompilerGCC() {
}

void CompilerGCC::OnAttach() {
  m_RealTargetsStartIndex = 0;
  m_RealTargetIndex = 0;
  m_PageIndex = -1;
  m_ListPageIndex = -1;
  m_Menu = 0L;
  m_TargetMenu = 0L;
  m_TargetIndex = -1;
  m_pErrorsMenu = 0L;
  m_pProject = 0L;
  m_pTbar = 0L;
  m_pLog = 0L;
  m_pListLog = 0L;
  m_pToolTarget = 0L;
  m_RunAfterCompile = false;
  m_LastExitCode = 0;
  m_NotifiedMaxErrors = false;
  m_pBuildingProject = 0;
  m_BuildJob = bjIdle;
  m_NextBuildState = bsNone;
  m_pLastBuildingProject = 0;
  m_pLastBuildingTarget = 0;
  m_RunTargetPostBuild = false;
  m_RunProjectPostBuild = false;
  m_Clean = false;
  m_Build = false;
  m_LastBuildStep = true;
  m_IsWorkspaceOperation = false;
  m_timerIdleWakeUp.SetOwner( this, idTimerPollCompiler );
  for( int i = 0; i < maxTargetInMenus; ++i ) {
    idMenuSelectTargetOther[i] = wxNewId();
  }
  DoRegisterCompilers();
  AllocProcesses();
  LogManager* msgMan = Manager::Get()->GetLogManager();
  m_pLog = new BuildLogger();
  m_PageIndex = msgMan->SetLog( m_pLog );
  msgMan->Slot( m_PageIndex ).title = _T( "编译日志" );
  wxString prefix = ConfigManager::GetDataFolder() + _T( "/images/" );
  wxBitmap* bmp = new wxBitmap( M_Code_LoadBitmap( prefix + _T( "misc.png" ), wxBITMAP_TYPE_PNG ) );
  msgMan->Slot( m_PageIndex ).icon = bmp;
  wxArrayString titles;
  wxArrayInt widths;
  titles.Add( _T( "文件" ) );
  titles.Add( _T( "行" ) );
  titles.Add( _T( "消息" ) );
  widths.Add( 228 );
  widths.Add( 100 );
  widths.Add( 840 );
  m_pListLog = new CompilerMessages( titles, widths );
  m_pListLog->SetCompilerErrors( &m_Errors );
  m_ListPageIndex = msgMan->SetLog( m_pListLog );
  msgMan->Slot( m_ListPageIndex ).title = _T( "构建消息" );
  bmp = new wxBitmap( M_Code_LoadBitmap( prefix + _T( "flag.png" ), wxBITMAP_TYPE_PNG ) );
  msgMan->Slot( m_ListPageIndex ).icon = bmp;
  CodeBlocksLogEvent evtAdd1( cbEVT_ADD_LOG_WINDOW, m_pLog, msgMan->Slot( m_PageIndex ).title, msgMan->Slot( m_PageIndex ).icon );
  Manager::Get()->ProcessEvent( evtAdd1 );
  if( !Manager::IsBatchBuild() ) {
    CodeBlocksLogEvent evtAdd2( cbEVT_ADD_LOG_WINDOW, m_pListLog, msgMan->Slot( m_ListPageIndex ).title, msgMan->Slot( m_ListPageIndex ).icon );
    Manager::Get()->ProcessEvent( evtAdd2 );
  }
  CompilerFactory::SetDefaultCompiler( Manager::Get()->GetConfigManager( _T( "compiler" ) )->Read( _T( "/default_compiler" ), _T( "gcc" ) ) );
  LoadOptions();
  Manager::Get()->GetScriptingManager();
  if( SquirrelVM::GetVMPtr() ) {
    ScriptBindings::gBuildLogId = m_PageIndex;
    SqPlus::RegisterGlobal( ScriptBindings::gBuildLog, "LogBuild" );
  } else {
    ScriptBindings::gBuildLogId = -1;
  }
  Manager::Get()->RegisterEventSink( cbEVT_PROJECT_ACTIVATE, new cbEventFunctor<CompilerGCC,
                                     CodeBlocksEvent>( this, &CompilerGCC::OnProjectActivated ) );
  Manager::Get()->RegisterEventSink( cbEVT_PROJECT_OPEN, new cbEventFunctor<CompilerGCC,
                                     CodeBlocksEvent>( this, &CompilerGCC::OnProjectLoaded ) );
  Manager::Get()->RegisterEventSink( cbEVT_PROJECT_CLOSE, new cbEventFunctor<CompilerGCC,
                                     CodeBlocksEvent>( this, &CompilerGCC::OnProjectUnloaded ) );
  Manager::Get()->RegisterEventSink( cbEVT_PROJECT_TARGETS_MODIFIED, new cbEventFunctor<CompilerGCC,
                                     CodeBlocksEvent>( this, &CompilerGCC::OnProjectActivated ) );
  Manager::Get()->RegisterEventSink( cbEVT_COMPILE_FILE_REQUEST, new cbEventFunctor<CompilerGCC,
                                     CodeBlocksEvent>( this, &CompilerGCC::OnCompileFileRequest ) );
}

void CompilerGCC::OnRelease( bool appShutDown ) {
  ScriptBindings::gBuildLogId = -1;
  SaveOptions();
  Manager::Get()->GetConfigManager( _T( "compiler" ) )->Write( _T( "/default_compiler" ), CompilerFactory::GetDefaultCompilerID() );
  if( Manager::Get()->GetLogManager() ) {
    if( !Manager::IsBatchBuild() ) {
      CodeBlocksLogEvent evt( cbEVT_REMOVE_LOG_WINDOW, m_pLog );
      Manager::Get()->ProcessEvent( evt );
    }
    m_pLog = 0;
    CodeBlocksLogEvent evt( cbEVT_REMOVE_LOG_WINDOW, m_pListLog );
    m_pListLog->DestroyControls();
    Manager::Get()->ProcessEvent( evt );
    m_pListLog = 0;
  }
  if( !appShutDown ) {
    DoClearTargetMenu();
  }
  m_timerIdleWakeUp.Stop();
  FreeProcesses();
  CompilerFactory::UnregisterCompilers();
}

int CompilerGCC::Configure( M_Code_Project* project, ProjectBuildTarget* target ) {
  cbConfigurationDialog dlg( Manager::Get()->GetAppWindow(), wxID_ANY, _T( "项目生成选项" ) );
  cbConfigurationPanel* panel = new CompilerOptionsDlg( &dlg, this, project, target );
  panel->SetParentDialog( &dlg );
  dlg.AttachConfigurationPanel( panel );
  PlaceWindow( &dlg );
  if( dlg.ShowModal() == wxID_OK ) {
    SaveOptions();
    Manager::Get()->GetMacrosManager()->Reset();
  }
  return 0;
}

cbConfigurationPanel* CompilerGCC::GetConfigurationPanel( wxWindow* parent ) {
  CompilerOptionsDlg* dlg = new CompilerOptionsDlg( parent, this, 0, 0 );
  return dlg;
}

void CompilerGCC::OnConfig( M_Code_unused wxCommandEvent& event ) {
  Configure( NULL );
}

void CompilerGCC::BuildModuleMenu( const ModuleType type, wxMenu* menu, const FileTreeData* data ) {
  if( !IsAttached() ) {
    return;
  }
  if( type != mtProjectManager || !menu ) {
    return;
  }
  if( !CheckProject() ) {
    return;
  }
  if( !data || data->GetKind() == FileTreeData::ftdkUndefined ) {
    menu->Append( idMenuBuildWorkspace, _T( "编译工作空间" ) );
    menu->Append( idMenuRebuildWorkspace, _T( "重构工作空间" ) );
    menu->Append( idMenuCleanWorkspace, _T( "清理工作空间" ) );
    if( IsRunning() ) {
      menu->Enable( idMenuBuildWorkspace, false );
      menu->Enable( idMenuRebuildWorkspace, false );
      menu->Enable( idMenuCleanWorkspace, false );
    }
  } else if( data && data->GetKind() == FileTreeData::ftdkProject ) {
    menu->Append( idMenuCompileFromProjectManager, _T( "编译" ) );
    menu->Append( idMenuRebuildFromProjectManager, _T( "重构" ) );
    menu->Append( idMenuCleanFromProjectManager, _T( "清理" ) );
    menu->Append( idMenuProjectCompilerOptionsFromProjectManager, _T( "编译参数" ) );
    M_Code_Plugin *otherRunning = Manager::Get()->GetProjectManager()->GetIsRunning();
    if( IsRunning() || ( otherRunning && otherRunning != this ) ) {
      menu->Enable( idMenuCompileFromProjectManager, false );
      menu->Enable( idMenuRebuildFromProjectManager, false );
      menu->Enable( idMenuCleanFromProjectManager, false );
      menu->Enable( idMenuProjectCompilerOptionsFromProjectManager, false );
    }
  } else if( data && data->GetKind() == FileTreeData::ftdkFile ) {
    FileType ft = FileTypeOf( data->GetProjectFile()->relativeFilename );
    if( ft == ftSource || ft == ftHeader || ft == ftTemplateSource ) {
      menu->Append( idMenuCompileFileFromProjectManager, _T( "编译文件" ) );
      menu->Append( idMenuCleanFileFromProjectManager, _T( "清理文件" ) );
      if( IsRunning() ) {
        menu->Enable( idMenuCompileFileFromProjectManager, false );
        menu->Enable( idMenuCleanFileFromProjectManager, false );
      }
    }
  }
}

bool CompilerGCC::BuildToolBar( wxToolBar* toolBar ) {
  if( !IsAttached() || !toolBar ) {
    return false;
  }
  m_pTbar = toolBar;
  wxXmlResource* xml_res = wxXmlResource::Get();
  wxString resPath = ConfigManager::GetDataFolder() + _T( "/Compiler_ToolBar.xrc" );
  if( !xml_res->Load( resPath ) )  {
    xml_File_lock = false;
    Manager::Get()->GetLogManager()->LogInf( F( _T( "文件丢失: %s " ), resPath.wx_str() ) );
    return false;
  }
  Manager::Get()->AddonToolBar( toolBar, _T( "compiler_toolbar" ) );
  xml_File_lock = true;
  m_pToolTarget = XRCCTRL( *toolBar, "idToolTarget", wxChoice );
  toolBar->Realize();
  toolBar->SetInitialSize();
  DoRecreateTargetMenu();
  return true;
}

void CompilerGCC::Dispatcher( wxCommandEvent& event ) {
  int eventId = event.GetId();
  if( eventId == idMenuRun ) {
    OnRun( event );
  } else if( eventId == idMenuCompileAndRun ) {
    OnCompileAndRun( event );
  } else if( eventId == idMenuCompile ) {
    OnCompile( event );
  } else if( eventId == idMenuCompileFromProjectManager ) {
    OnCompile( event );
  } else if( eventId == idMenuCompileFile ) {
    OnCompileFile( event );
  } else if( eventId == idMenuCompileFileFromProjectManager ) {
    OnCompileFile( event );
  } else if( eventId == idMenuCleanFileFromProjectManager ) {
    OnCleanFile( event );
  } else if( eventId == idMenuRebuild ) {
    OnRebuild( event );
  } else if( eventId == idMenuRebuildFromProjectManager ) {
    OnRebuild( event );
  } else if( eventId == idMenuBuildWorkspace ) {
    OnCompileAll( event );
  } else if( eventId == idMenuRebuildWorkspace ) {
    OnRebuildAll( event );
  } else if( eventId == idMenuProjectCompilerOptions || eventId == idMenuProjectCompilerOptionsFromProjectManager ) {
    OnProjectCompilerOptions( event );
  } else if( eventId == idMenuTargetCompilerOptions ) {
    OnTargetCompilerOptions( event );
  } else if( eventId == idMenuClean ) {
    OnClean( event );
  } else if( eventId == idMenuCleanWorkspace ) {
    OnCleanAll( event );
  } else if( eventId == idMenuCleanFromProjectManager ) {
    OnClean( event );
  } else if( eventId == idMenuKillProcess ) {
    OnKillProcess( event );
  } else if( eventId == idMenuNextError ) {
    OnNextError( event );
  } else if( eventId == idMenuPreviousError ) {
    OnPreviousError( event );
  } else if( eventId == idMenuClearErrors ) {
    OnClearErrors( event );
  } else if( eventId == idMenuSettings ) {
    OnConfig( event );
  }
  M_Code_Editor* ed = 0;
  if( ( ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor() ) ) {
    ed->GetControl()->SetFocus();
  }
}

void CompilerGCC::TextURL( wxTextUrlEvent& event ) {
  if( event.GetId() == idBuildLog && event.GetMouseEvent().ButtonDown( wxMOUSE_BTN_LEFT ) ) {
    m_pLog->OpenLink( event.GetURLStart(), event.GetURLEnd() );
  } else {
    event.Skip();
  }
}

void CompilerGCC::SetupEnvironment() {
  if( m_CompilerId == _T( "null" ) ) {
    return;
  }
  Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
  if( !compiler ) {
    return;
  }
  wxString currentPath;
  if( !wxGetEnv( _T( "PATH" ), &currentPath ) ) {
    InfoWindow::Tip( _T( "无法读取路径环境变量 真糟糕 运行系统命令可能有问题 应用程序可能无法运行" ), _T( "环境错误" ), 15000, 3000 );
    return;
  }
  const wxString pathApp =  _T( ":" );
  const wxString pathSep = wxFileName::GetPathSeparator();
  const bool caseSens = true;
  wxString cApp = compiler->GetPrograms().C;
  Manager::Get()->GetMacrosManager()->ReplaceMacros( cApp );
  wxArrayString extraPaths = compiler->GetExtraPaths();
  wxString extraPathsBinPath( wxEmptyString );
  wxString masterPath = compiler->GetMasterPath();
  Manager::Get()->GetMacrosManager()->ReplaceMacros( masterPath );
  while( !masterPath.IsEmpty() && ( ( masterPath.Last() == '\\' ) || ( masterPath.Last() == '/' ) ) ) {
    masterPath.RemoveLast();
  }
  wxPathList pathList;
  if( !masterPath.Trim().IsEmpty() ) {
    pathList.Add( masterPath + pathSep + _T( "bin" ) );
    pathList.Add( masterPath );
  }
  for( size_t i = 0; i < extraPaths.GetCount(); ++i ) {
    wxString extraPath = extraPaths[i];
    Manager::Get()->GetMacrosManager()->ReplaceMacros( extraPath );
    while( extraPath.Last() == '\\' || extraPath.Last() == '/' ) {
      extraPath.RemoveLast();
    }
    if( !extraPath.Trim().IsEmpty() ) {
      if( extraPathsBinPath.IsEmpty() && wxFileExists( extraPath + pathSep + cApp ) ) {
        extraPathsBinPath = extraPath;
      }
      pathList.Add( extraPath );
    }
  }
  wxPathList pathArray;
  pathArray.AddEnvList( _T( "PATH" ) );
  pathList.Add( pathArray );
  wxString binPath = pathList.FindAbsoluteValidPath( cApp );
  if( binPath.IsEmpty() || ( pathList.Index( wxPathOnly( binPath ), caseSens ) == wxNOT_FOUND ) ) {
    if( wxFileExists( masterPath + pathSep + _T( "bin" ) + pathSep + cApp ) ) {
      binPath = masterPath + pathSep + _T( "bin" );
    } else if( wxFileExists( masterPath + pathSep + cApp ) ) {
      binPath = masterPath;
    } else if( !extraPathsBinPath.IsEmpty() ) {
      binPath = extraPathsBinPath;
    }
  } else {
    binPath = wxPathOnly( binPath );
  }
  if( ( binPath.IsEmpty() || ( pathList.Index( binPath, caseSens ) == wxNOT_FOUND ) ) ) {
    InfoWindow::Tip( _T( "该" ) + compiler->GetName() + _T( "找不到编译器可执行文件" ), _T( "环境错误" ), 1500 );
    return;
  }
  wxString envPath( binPath );
  pathList.Remove( binPath );
  for( size_t i = 0; i < pathList.GetCount(); ++i ) {
    envPath += ( pathApp + pathList[i] );
  }
  if( !wxSetEnv( _T( "PATH" ), envPath ) ) {
    InfoWindow::Tip( _T( "无法设置环境变量 这很糟糕 编译器可能无法工作" ), _T( "环境错误" ) );
  }
}

bool CompilerGCC::StopRunningDebugger() {
  cbDebuggerPlugin *dbg = Manager::Get()->GetDebuggerManager()->GetActiveDebugger();
  if( dbg && dbg->IsRunning() ) {
    int ret = msging( _T( "必须停止调试器才能进行（重新）生成.\n 现在要停止调试器吗?" ), _T( "Information" ),
                      wxYES_NO | wxCANCEL | wxICON_QUESTION );
    switch( ret ) {
      case wxID_YES: {
        m_pLog->Clear();
        Manager::Get()->GetLogManager()->Log( _T( "正在停止调试器..." ), m_PageIndex );
        dbg->Stop();
        break;
      }
      case wxID_NO:
      default:
        Manager::Get()->GetLogManager()->Log( _T( "正在中止（重新）生成." ), m_PageIndex );
        return false;
    }
  }
  return true;
}

void CompilerGCC::SaveOptions() {
  CompilerFactory::SaveSettings();
}

void CompilerGCC::LoadOptions() {
  CompilerFactory::LoadSettings();
}

void CompilerGCC::DoRegisterCompilers() {  // 注册编译器
  CompilerFactory::RegisterCompiler( new CompilerC );
  wxDir dir;
  wxString filename;
  wxArrayString compilers;
  wxString path = ConfigManager::GetFolder( sdDataGlobal ) + _T( "/compilers/" );
  if( wxDirExists( path ) && dir.Open( path ) ) {
    bool ok = dir.GetFirst( &filename, _T( "compiler_*.xml" ), wxDIR_FILES );
    while( ok ) {
      for( size_t i = 0; i < compilers.GetCount(); ++i ) {
        if( compilers[i].EndsWith( filename ) ) {
          ok = false;
          break;
        }
      }
      if( ok ) {
        compilers.Add( path + filename );
      }
      ok = dir.GetNext( &filename );
    }
  }
  for( size_t i = 0; i < compilers.GetCount(); ++i ) {
    wxXmlDocument compiler;
    if( !compiler.Load( compilers[i] ) || compiler.GetRoot()->GetName() != _T( "m_Code_compiler" ) ) {
      Manager::Get()->GetLogManager()->LogError( _T( "错误: 编译器定义无效 '" ) + compilers[i] + _T( "'." ) );
    } else {
      CompilerFactory::RegisterCompiler(
        new CompilerXML( compiler.GetRoot()->GetAttribute( _T( "name" ), wxEmptyString ),
                         compiler.GetRoot()->GetAttribute( _T( "id" ), wxEmptyString ),
                         compilers[i] ) );
    }
  }
  CompilerFactory::RegisterUserCompilers();
}

const wxString& CompilerGCC::GetCurrentCompilerID() {
  static wxString def = wxEmptyString;
  return CompilerFactory::GetCompiler( m_CompilerId ) ? m_CompilerId : def;
}

void CompilerGCC::SwitchCompiler( const wxString& id ) {
  if( !CompilerFactory::GetCompiler( id ) ) {
    return;
  }
  m_CompilerId = id;
  SetupEnvironment();
}

void CompilerGCC::PrepareCompileFilePM( wxFileName& file ) {
  FileTreeData* ftd = DoSwitchProjectTemporarily();
  ProjectFile* pf = ftd->GetProjectFile();
  if( !pf ) {
    return;
  }
  file = pf->file;
  CheckProject();
}

void CompilerGCC::PrepareCompileFile( wxFileName& file ) {
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  if( ed ) {
    ed->Save();
    file.Assign( ed->GetFilename() );
  }
  ProjectFile* pf = ed->GetProjectFile();
  if( pf ) {
    M_Code_Project* CurProject = pf->GetParentProject();
    if( CurProject ) {
      Manager::Get()->GetProjectManager()->SetProject( CurProject, true );
      CheckProject();
    }
  }
}

bool CompilerGCC::CheckProject() {
  AskForActiveProject();
  if( m_pProject && m_pProject->GetCompilerID() != m_CompilerId ) {
    SwitchCompiler( m_pProject->GetCompilerID() );
  } else if( !m_pProject && m_CompilerId != CompilerFactory::GetDefaultCompilerID() ) {
    SwitchCompiler( CompilerFactory::GetDefaultCompilerID() );
  }
  return ( m_pProject != 0L );
}

void CompilerGCC::AskForActiveProject() {
  m_pProject = m_pBuildingProject ? m_pBuildingProject : Manager::Get()->GetProjectManager()->GetActiveProject();
}

void CompilerGCC::StartCompileFile( wxFileName file ) {
  if( m_pProject ) {
    if( !m_pProject->SaveAllFiles() ) {
      Manager::Get()->GetLogManager()->Log( _T( "无法保存所有文件..." ) );
    }
    file.MakeRelativeTo( m_pProject->GetBasePath() );
  }
  wxString fname = file.GetFullPath();
  if( !fname.IsEmpty() ) {
    CodeBlocksLogEvent evtSwitch( cbEVT_SWITCH_TO_LOG_WINDOW, m_pLog );
    Manager::Get()->ProcessEvent( evtSwitch );
    CompileFile( UnixFilename( fname ) );
  }
}

wxString CompilerGCC::ProjectMakefile() {
  AskForActiveProject();
  if( !m_pProject ) {
    return wxEmptyString;
  }
  return m_pProject->GetMakefile();
}

void CompilerGCC::ClearLog() {
  if( m_IsWorkspaceOperation ) {
    return;
  }
  if( IsProcessRunning() ) {
    return;
  }
  CodeBlocksLogEvent evtSwitch( cbEVT_SWITCH_TO_LOG_WINDOW, m_pLog );
  Manager::Get()->ProcessEvent( evtSwitch );
  if( m_pLog ) {
    m_pLog->Clear();
  }
}

FileTreeData* CompilerGCC::DoSwitchProjectTemporarily() {
  ProjectManager* manager = Manager::Get()->GetProjectManager();
  wxTreeCtrl* tree = manager->GetUI().GetTree();
  wxTreeItemId sel = manager->GetUI().GetTreeSelection();
  FileTreeData* ftd = sel.IsOk() ? ( FileTreeData* )tree->GetItemData( sel ) : nullptr;
  if( !ftd ) {
    return nullptr;
  }
  Manager::Get()->GetProjectManager()->SetProject( ftd->GetProject(), false );
  AskForActiveProject();
  return ftd;
}

void CompilerGCC::AddToCommandQueue( const wxArrayString& commands ) {
  ProjectBuildTarget* bt = m_pBuildingProject ? m_pBuildingProject->GetBuildTarget( GetTargetIndexFromName( m_pBuildingProject, m_BuildingTargetName ) ) : 0;
  m_CurrentProgress = 0;
  m_MaxProgress = 0;
  bool isLink = false;
  bool mustWait = false;
  size_t count = commands.GetCount();
  for( size_t i = 0; i < count; ++i ) {
    wxString cmd = commands[i];
    if( cmd.StartsWith( COMPILER_SIMPLE_LOG ) ) {
      cmd.Remove( 0, COMPILER_SIMPLE_LOG.Length() );
      m_CommandQueue.Add( new CompilerCommand( wxEmptyString, cmd, m_pBuildingProject, bt ) );
    } else if( cmd.StartsWith( COMPILER_TARGET_CHANGE ) )
      ;
    else if( cmd.StartsWith( COMPILER_WAIT ) ) {
      mustWait = true;
    } else if( cmd.StartsWith( COMPILER_WAIT_LINK ) ) {
      isLink = true;
    } else {
      CompilerCommand* p = new CompilerCommand( cmd, wxEmptyString, m_pBuildingProject, bt );
      p->mustWait = mustWait;
      p->isLink = isLink;
      m_CommandQueue.Add( p );
      isLink = false;
      mustWait = false;
      ++m_MaxProgress;
    }
  }
  if( m_pLog->progress ) {
    m_pLog->progress->SetRange( m_MaxProgress );
    m_pLog->progress->SetValue( m_CurrentProgress );
  }
}

void CompilerGCC::AllocProcesses() {
  size_t parallel_processes = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadInt( _T( "/parallel_processes" ), 2 );
  if( parallel_processes == 0 ) {
    parallel_processes = std::max( 1, wxThread::GetCPUCount() );
  }
  m_CompilerProcessList.resize( parallel_processes );
  for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
    m_CompilerProcessList.at( i ).pProcess = 0;
    m_CompilerProcessList.at( i ).PID      = 0;
  }
}

void CompilerGCC::FreeProcesses() {
  for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
    Delete( m_CompilerProcessList.at( i ).pProcess );
  }
  m_CompilerProcessList.clear();
}

bool CompilerGCC::ReAllocProcesses() {
  FreeProcesses();
  AllocProcesses();
  return true;
}

bool CompilerGCC::IsProcessRunning( int idx ) const {
  if( m_CompilerProcessList.empty() || idx >= ( int )m_CompilerProcessList.size() ) {
    return false;
  }
  if( idx >= 0 ) {
    return ( m_CompilerProcessList.at( static_cast<size_t>( idx ) ).pProcess != 0 );
  }
  for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
    if( m_CompilerProcessList.at( i ).pProcess != 0 ) {
      return true;
    }
  }
  return false;
}

int CompilerGCC::GetNextAvailableProcessIndex() const {
  for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
    if( ( m_CompilerProcessList.at( i ).pProcess == 0 ) && ( m_CompilerProcessList.at( i ).PID == 0 ) ) {
      return i;
    }
  }
  return -1;
}

int CompilerGCC::GetActiveProcessCount() const {
  size_t count = 0;
  for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
    if( m_CompilerProcessList.at( i ).pProcess != 0 ) {
      ++count;
    }
  }
  return count;
}

int CompilerGCC::DoRunQueue() {
  int procIndex = GetNextAvailableProcessIndex();
  if( procIndex == -1 ) {
    return -2;
  }
  if( IsProcessRunning() ) {
    CompilerCommand* cmd = m_CommandQueue.Peek();
    if( cmd && ( cmd->mustWait || cmd->isLink ) ) {
      return -3;
    }
  }
  CompilerCommand* cmd = m_CommandQueue.Next();
  if( !cmd ) {
    if( IsProcessRunning() ) {
      return 0;
    }
    while( 1 ) {
      BuildStateManagement();
      cmd = m_CommandQueue.Next();
      if( !cmd && m_BuildState == bsNone && m_NextBuildState == bsNone ) {
        NotifyJobDone( true );
        ResetBuildState();
        if( m_RunAfterCompile ) {
          m_RunAfterCompile = false;
          if( Run() == 0 ) {
            DoRunQueue();
          }
        }
        return 0;
      }
      if( cmd ) {
        break;
      }
    }
  }
  wxString dir = cmd->dir;
  bool hasLog = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/save_html_build_log" ), false );
  bool saveFull = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/save_html_build_log/full_command_line" ), false );
  if( hasLog ) {
    if( !cmd->command.IsEmpty() && saveFull ) {
      LogMessage( cmd->command, cltNormal, ltFile );
    } else if( !cmd->message.IsEmpty() && !saveFull ) {
      LogMessage( cmd->message, cltNormal, ltFile );
    }
  }
  if( !cmd->message.IsEmpty() ) {
    LogMessage( cmd->message, cltNormal, ltMessages, false, false, true );
  }
  if( cmd->command.IsEmpty() ) {
    int ret = DoRunQueue();
    delete cmd;
    return ret;
  } else if( cmd->command.StartsWith( _T( "#run_script" ) ) ) {
    wxString script = cmd->command.AfterFirst( _T( ' ' ) );
    if( script.IsEmpty() ) {
      wxString msg = _T( "必须在脚本文件名后面跟着" );
      LogMessage( msg, cltError );
    } else {
      Manager::Get()->GetMacrosManager()->ReplaceMacros( script );
      wxString msg = _T( "运行脚本: " ) + script;
      LogMessage( msg );
      Manager::Get()->GetScriptingManager()->LoadScript( script );
    }
    int ret = DoRunQueue();
    delete cmd;
    return ret;
  }
  wxString oldLibPath;
  wxGetEnv( CB_LIBRARY_ENVVAR, &oldLibPath );
  bool pipe = true;
  int flags = wxEXEC_ASYNC;
  if( cmd->isRun ) {
    pipe = false;
    flags |= wxEXEC_NOHIDE;
    dir = m_CdRun;
    wxString newLibPath = cbGetDynamicLinkerPathForTarget( m_pProject, cmd->target );
    newLibPath = cbMergeLibPaths( oldLibPath, newLibPath );
    wxSetEnv( CB_LIBRARY_ENVVAR, newLibPath );
  }
  if( !cmd->isRun ) {
    ExpandBackticks( cmd->command );
    wxString shell = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/console_shell" ), DEFAULT_CONSOLE_SHELL );
    cmd->command = shell + _T( " '" ) + cmd->command + _T( "'" );
  }
  CompilerProcess &process = m_CompilerProcessList.at( procIndex );
  process.OutputFile = ( cmd->isLink && cmd->target ) ? cmd->target->GetOutputFilename() : wxString( wxEmptyString );
  process.pProcess = new PipedProcess( &( process.pProcess ), this, idGCCProcess, pipe, dir, procIndex );
  process.PID = wxExecute( cmd->command, flags, process.pProcess );
  if( !process.PID ) {
    wxString err = F( _T( "执行 '%s' in '%s' 失败." ), cmd->command.wx_str(), wxGetCwd().wx_str() );
    LogMessage( err, cltError );
    LogWarningOrError( cltError, 0, wxEmptyString, wxEmptyString, err );
    if( !m_CommandQueue.LastCommandWasRun() ) {
      if( !IsProcessRunning() ) {
        wxString msg = F( _T( "%s (%s)" ), GetErrWarnStr().wx_str(), GetMinSecStr().wx_str() );
        LogMessage( msg, cltError, ltAll, true );
        LogWarningOrError( cltNormal, 0, wxEmptyString, wxEmptyString, F( _T( "=== 生成失败: %s ===" ), msg.wx_str() ) );
        m_pListLog->AutoFitColumns( 2 );
        SaveBuildLog();
      }
      if( !Manager::IsBatchBuild() && m_pLog->progress ) {
        m_pLog->progress->SetValue( 0 );
      }
    }
    Delete( process.pProcess );
    m_CommandQueue.Clear();
    ResetBuildState();
  } else {
    m_timerIdleWakeUp.Start( 100 );
  }
  wxSetEnv( CB_LIBRARY_ENVVAR, oldLibPath );
  delete cmd;
  return DoRunQueue();
}

void CompilerGCC::DoClearTargetMenu() {
  if( m_TargetMenu ) {
    wxMenuItemList& items = m_TargetMenu->GetMenuItems();
    while( wxMenuItemList::Node* node = items.GetFirst() ) {
      if( node->GetData() ) {
        m_TargetMenu->Delete( node->GetData() );
      }
    }
  }
}

bool CompilerGCC::IsValidTarget( const wxString &target ) const {
  if( target.IsEmpty() ) {
    return false;
  }
  if( m_Targets.Index( target ) == -1 ) {
    return false;
  }
  const ProjectBuildTarget* tgt = Manager::Get()->GetProjectManager()->GetActiveProject()->GetBuildTarget( target );
  if( tgt ) {
    return false;
  }
  return true;
}

void CompilerGCC::DoRecreateTargetMenu() {
  if( !IsAttached() ) {
    return;
  }
  if( m_pToolTarget ) {
    m_pToolTarget->Freeze();
  }
  wxMenuBar* mbar = Manager::Get()->GetAppFrame()->GetMenuBar();
  if( mbar ) {
    mbar->Freeze();
  }
  do {
    DoClearTargetMenu();
    if( m_pToolTarget ) {
      m_pToolTarget->Clear();
    }
    if( !CheckProject() ) {
      break;
    }
    if( !m_Targets.GetCount() ) {
      break;
    }
    wxString tgtStr( m_pProject->GetFirstValidBuildTargetName() );
    if( M_Code_Workspace* wsp = Manager::Get()->GetProjectManager()->GetWorkspace() ) {
      const wxString preferredTarget = wsp->GetPreferredTarget();
      tgtStr = preferredTarget;
      if( !IsValidTarget( tgtStr ) ) {
        tgtStr = m_pProject->GetActiveBuildTarget();
      }
      if( !IsValidTarget( tgtStr ) ) {
        tgtStr = m_pProject->GetFirstValidBuildTargetName();
      }
      if( preferredTarget.IsEmpty() ) {
        wsp->SetPreferredTarget( tgtStr );
      }
    }
    if( m_TargetMenu ) {
      m_TargetMenu->Append( idMenuSelectTargetDialog, _T( "选择目标..." ) );
    }
    for( size_t x = 0; x < m_Targets.GetCount(); ++x ) {
      if( m_TargetMenu && x < maxTargetInMenus ) {
        wxString help;
        help.Printf( _T( "建立目标 '%s' 到当前项目" ), GetTargetString( x ).wx_str() );
        m_TargetMenu->AppendCheckItem( idMenuSelectTargetOther[x], GetTargetString( x ), help );
      }
      if( m_pToolTarget ) {
        m_pToolTarget->Append( GetTargetString( x ) );
      }
    }
    if( m_TargetMenu && m_Targets.size() > maxTargetInMenus ) {
      m_TargetMenu->Append( idMenuSelectTargetHasMore, _T( "更多可用目标..." ) );
      m_TargetMenu->Enable( idMenuSelectTargetHasMore, false );
    }
    Connect( idMenuSelectTargetOther[0], idMenuSelectTargetOther[maxTargetInMenus - 1],
             wxEVT_COMMAND_MENU_SELECTED,
             ( wxObjectEventFunction )( wxEventFunction )( wxCommandEventFunction )
             &CompilerGCC::OnSelectTarget );
    m_TargetIndex = m_Targets.Index( tgtStr );
    m_RealTargetIndex = m_TargetIndex - m_RealTargetsStartIndex;
    if( m_RealTargetIndex < 0 ) {
      m_RealTargetIndex = -1;
    }
    DoUpdateTargetMenu( m_TargetIndex );
    if( m_pToolTarget ) {
      m_pToolTarget->SetSelection( m_TargetIndex );
    }
    SwitchCompiler( m_pProject->GetCompilerID() );
  } while( false );
  if( mbar ) {
    mbar->Thaw();
  }
  if( m_pToolTarget ) {
    m_pToolTarget->Thaw();
  }
}

void CompilerGCC::DoUpdateTargetMenu( int targetIndex ) {
  m_TargetIndex = targetIndex;
  m_RealTargetIndex = m_TargetIndex - m_RealTargetsStartIndex;
  if( m_RealTargetIndex < 0 ) {
    m_RealTargetIndex = -1;
  }
  if( m_TargetIndex == -1 ) {
    m_TargetIndex = 0;
  }
  if( m_pProject ) {
    m_pProject->SetActiveBuildTarget( GetTargetString( m_TargetIndex ) );
  }
  if( m_TargetMenu ) {
    for( int i = 0; i < maxTargetInMenus; ++i ) {
      wxMenuItem* item = m_TargetMenu->FindItem( idMenuSelectTargetOther[i] );
      if( !item || !item->IsCheckable() ) {
        continue;
      }
      item->Check( i == m_TargetIndex );
    }
  }
}

void CompilerGCC::UpdateProjectTargets( M_Code_Project* project ) {
  m_Targets.Clear();
  if( !project ) {
    return;
  }
  wxArrayString virtuals = project->GetVirtualBuildTargets();
  for( size_t i = 0; i < virtuals.GetCount(); ++i ) {
    m_Targets.Add( virtuals[i] );
  }
  for( int i = 0; i < project->GetBuildTargetsCount(); ++i ) {
    ProjectBuildTarget *tgt = project->GetBuildTarget( i );
    m_Targets.Add( tgt->GetTitle() ); //??????????????
  }
  m_RealTargetsStartIndex = virtuals.GetCount();
  DoRecreateTargetMenu();
}

wxString CompilerGCC::GetTargetString( int index ) {
  if( index == -1 ) {
    index = m_TargetIndex;
  }
  if( index >= 0 && index < ( int )m_Targets.GetCount() ) {
    return m_Targets[index];
  }
  return wxEmptyString;
}

void CompilerGCC::DoPrepareQueue( bool clearLog ) {
  if( m_CommandQueue.GetCount() == 0 ) {
    CodeBlocksEvent evt( cbEVT_COMPILER_STARTED, 0, m_pProject, 0, this );
    Manager::Get()->ProcessEvent( evt );
    if( clearLog ) {
      ClearLog();
      DoClearErrors();
    }
    m_StartTime = wxGetLocalTimeMillis();
  }
  Manager::Yield();
}

void CompilerGCC::NotifyCleanProject( const wxString& target ) {
  if( m_CommandQueue.GetCount() == 0 ) {
    CodeBlocksEvent evt( cbEVT_CLEAN_PROJECT_STARTED, 0, m_pProject, 0, this );
    evt.SetBuildTargetName( target );
    Manager::Get()->ProcessEvent( evt );
  }
  Manager::Yield();
}

void CompilerGCC::NotifyCleanWorkspace() {
  if( m_CommandQueue.GetCount() == 0 ) {
    CodeBlocksEvent evt( cbEVT_CLEAN_WORKSPACE_STARTED, 0, 0, 0, this );
    Manager::Get()->ProcessEvent( evt );
  }
  Manager::Yield();
}

ProjectBuildTarget* CompilerGCC::DoAskForTarget() {
  if( !CheckProject() ) {
    return 0L;
  }
  return m_pProject->GetBuildTarget( m_RealTargetIndex );
}

int CompilerGCC::DoGUIAskForTarget() {
  if( !CheckProject() ) {
    return -1;
  }
  return m_pProject->SelectTarget( m_RealTargetIndex );
}

bool CompilerGCC::UseMake( M_Code_Project* project ) {
  if( !project ) {
    project = m_pProject;
  }
  if( !project ) {
    return false;
  }
  wxString idx = project->GetCompilerID();
  if( CompilerFactory::GetCompiler( idx ) ) {
    return project->IsMakefileCustom();
  }
  return false;
}

wxString CompilerGCC::GetCurrentCompilerID( ProjectBuildTarget* target ) {
  if( target ) {
    return target->GetCompilerID();
  }
  if( m_pBuildingProject ) {
    return m_pBuildingProject->GetCompilerID();
  }
  if( m_pProject ) {
    return m_pProject->GetCompilerID();
  }
  return wxEmptyString;
}

auto CompilerGCC::CompilerValid( ProjectBuildTarget* target ) -> CompilerValidResult {
  CompilerValidResult result;
  if( !target ) {
    result.compiler = CompilerFactory::GetDefaultCompiler();
  } else {
    wxString idx = GetCurrentCompilerID( target );
    result.compiler = CompilerFactory::GetCompiler( idx );
  }
  if( result.compiler ) {
    result.isValid = result.compiler->IsValid();
  }
  return result;
}

void CompilerGCC::PrintInvalidCompiler( ProjectBuildTarget *target, Compiler *compiler, const wxString &finalMessage ) {
  wxString compilerName, compilerName2( _T( "unknown" ) );
  if( compiler ) {
    compilerName = _T( "(" ) + compiler->GetName() + _T( ") " );
    compilerName2 = compiler->GetName();
  }
  wxString title;
  if( target ) {
    title = target->GetFullTitle();
  } else {
    title = _T( "unknown" );
  }
  wxString msg;
  msg.Printf( _T( "项目/目标: \"%s\":\n  编译器 %s 的配置无效" ),
              title.wx_str(), compilerName.wx_str(), compilerName2.wx_str() );
  LogManager *logger = Manager::Get()->GetLogManager();
  logger->LogError( msg, m_PageIndex );
  if( compiler ) {
    logger->LogError( compiler->MakeInvalidCompilerMessages(), m_PageIndex );
  }
  logger->LogError( finalMessage, m_PageIndex );
}

void CompilerGCC::PrintBanner( BuildAction action, M_Code_Project* prj, ProjectBuildTarget* target ) {
  if( !CompilerValid( target ).isValid ) {
    return;
  }
  CodeBlocksLogEvent evtShow( cbEVT_SHOW_LOG_MANAGER );
  Manager::Get()->ProcessEvent( evtShow );
  if( !prj ) {
    prj = m_pProject;
  }
  wxString Action;
  switch( action ) {
    case baClean:
      Action = _T( "清理" );
      break;
    case baRun:
      Action = _T( "运行" );
      break;
    case baBuildFile:
      Action = _T( "编译文件" );
      break;
    default:
    case baBuild:
      Action = _T( "编译" );
      break;
  }
  wxString compilerName;
  Compiler *compiler = CompilerFactory::GetCompiler( GetCurrentCompilerID( target ) );
  if( compiler ) {
    compilerName = compiler->GetName();
  }
  wxString targetName = target ? target->GetTitle() : wxString( _T( "\"无目标\"" ) );
  wxString projectName = prj ? prj->GetTitle() : wxString( _T( "\"无项目\"" ) );
  wxString banner;
  banner.Printf( _T( "-------------- %s: %s in %s (编译器: %s)---------------" ),
                 Action.wx_str(), targetName.wx_str(), projectName.wx_str(), compilerName.wx_str() );
  LogWarningOrError( cltNormal, 0, wxEmptyString, wxEmptyString, _T( "===          " ) + banner + _T( "          ===" ) );
  LogMessage( _T( "--------------" ) + banner + _T( "---------------" ), cltNormal, ltAll, false, true );
  m_pListLog->AutoFitColumns( 2 );
}

void CompilerGCC::DoGotoNextError() {
  m_Errors.Next();
  m_pListLog->FocusError( m_Errors.GetFocusedError() );
}

void CompilerGCC::DoGotoPreviousError() {
  m_Errors.Previous();
  m_pListLog->FocusError( m_Errors.GetFocusedError() );
}

void CompilerGCC::DoClearErrors() {
  m_Errors.Clear();
  m_pListLog->Clear();
  m_NotifiedMaxErrors = false;
}

int CompilerGCC::RunSingleFile( const wxString& filename ) {
  wxFileName fname( filename );
  static bool Build_lock = true;
  if( fname.GetExt() == _T( "script" ) ) {
    Manager::Get()->GetScriptingManager()->LoadScript( filename );
    return 0;
  }
  if( fname.GetExt() == FileFilters::C_EXT ||
      fname.GetExt() == FileFilters::CC_EXT ||
      fname.GetExt() == FileFilters::CPP_EXT ||
      fname.GetExt() == FileFilters::CXX_EXT
    ) {
    m_CdRun = fname.GetPath();
    fname.SetExt( FileFilters::EXECUTABLE_EXT );
    wxString exe_filename = fname.GetFullPath();
    wxString command;
    wxString term = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/console_terminal" ), DEFAULT_CONSOLE_TERM );
    term.Replace( _T( "$TITLE" ), _T( "'" ) + exe_filename + _T( "'" ) );
    command << term << strSPACE;
    wxString baseDir = ConfigManager::GetExecutableFolder();
    wxString crunnStr = strQUOTE + baseDir + strSLASH + strCONSOLE_RUNNER + strQUOTE;
    if( wxFileExists( baseDir + strSLASH + strCONSOLE_RUNNER ) ) {
      command << crunnStr << strSPACE;
    }
    if( !command.Replace( _T( "$SCRIPT" ), exe_filename ) ) {
      command << strQUOTE << exe_filename << strQUOTE;
    }
    Manager::Get()->GetLogManager()->LogWarning( _T( "检查存在性: " ) + exe_filename, m_PageIndex );
    if( !wxFileExists( exe_filename ) ) {
      if( !Build_lock ) {
        return 0;
      }
      InfoWindow::Tip( _T( "文件丢失了... 已自动重新编译文件" ), _T( "提示" ), 1500 );
      m_RunAfterCompile = true;
      Build( wxEmptyString );
      Build_lock = false;
    }
    Manager::Get()->GetMacrosManager()->ReplaceEnvVars( m_CdRun );
    Manager::Get()->GetLogManager()->Log( F( _T( "执行: '%s' (in '%s')" ), command.wx_str(), m_CdRun.wx_str() ), m_PageIndex );
    m_CommandQueue.Add( new CompilerCommand( command, wxEmptyString, 0, 0, true ) );
    return 0;
  }
  return -1;
}

int CompilerGCC::Run( const wxString& target ) {
  if( !CheckProject() ) {
    return -1;
  }
  return Run( m_pProject->GetBuildTarget( target.IsEmpty() ? m_LastTargetName : target ) );
}

int CompilerGCC::Run( ProjectBuildTarget* target ) {
  bool commandIsQuoted = false;
  if( !CheckProject() ) {
    if( Manager::Get()->GetEditorManager()->GetActiveEditor() ) {
      return RunSingleFile( Manager::Get()->GetEditorManager()->GetActiveEditor()->GetFilename() );
    }
    return -1;
  } else {
    target = m_pProject->GetBuildTarget( m_pProject->GetActiveBuildTarget() );
  }
  PrintBanner( baRun, m_pProject, target );
  DoPrepareQueue( false );
  if( !( target && ( target->GetTargetType() == ttCommandsOnly || target->GetCompilerID() == _T( "null" ) ) ) ) {
    CompilerValidResult result = CompilerValid( target );
    if( !result.isValid ) {
      PrintInvalidCompiler( target, result.compiler, _T( "中止运行..." ) );
      return -1;
    }
  }
  if( !target ) {
    if( m_RealTargetIndex == -1 ) {
      int idx = -1;
      int bak = m_RealTargetIndex;
      if( m_pProject->GetBuildTargetsCount() == 1 ) {
        idx = 0;
      } else {
        idx = DoGUIAskForTarget();
      }
      m_RealTargetIndex = idx;
      target = DoAskForTarget();
      m_RealTargetIndex = bak;
    } else {
      target = DoAskForTarget();
    }
  }
  if( !target ) {
    return -1;
  }
  m_pProject->SetCurrentlyCompilingTarget( target );
  wxString out = UnixFilename( target->GetOutputFilename() );
  Manager::Get()->GetMacrosManager()->ReplaceEnvVars( out );
  wxString cmd;
  wxString command;
  wxFileName f( out );
  f.MakeAbsolute( m_pProject->GetBasePath() );
  m_CdRun = target->GetWorkingDir();
  Manager::Get()->GetMacrosManager()->ReplaceEnvVars( m_CdRun );
  wxFileName cd( m_CdRun );
  if( cd.IsRelative() ) {
    cd.MakeAbsolute( m_pProject->GetBasePath() );
  }
  m_CdRun = cd.GetFullPath();
  wxString baseDir = ConfigManager::GetExecutableFolder();
  wxString titleStr =  EscapeSpaces( m_pProject->GetTitle() );
  wxString dirStr =  EscapeSpaces( m_CdRun );
  wxString crunnStr =  EscapeSpaces( baseDir + strSLASH + strCONSOLE_RUNNER );
  wxString hostapStr =  EscapeSpaces( target->GetHostApplication() );
  wxString execStr =  EscapeSpaces( f.GetFullPath() );
  if( target->GetTargetType() == ttConsoleOnly || target->GetRunHostApplicationInTerminal() ) {
    wxString term = Manager::Get()->GetConfigManager( _T( "app" ) )->Read( _T( "/console_terminal" ), DEFAULT_CONSOLE_TERM );
    term.Replace( _T( "$TITLE" ), titleStr );
    term.Replace( _T( "$WORKDIR" ), dirStr );
    cmd << term << strSPACE;
    wxString shell;
    wxGetEnv( _T( "SHELL" ), &shell );
    if( shell.Contains( _T( "csh" ) ) ) {
      command << DEFAULT_CONSOLE_SHELL << strSPACE;
      command << strQUOTE;
      commandIsQuoted = true;
    }
    if( target->GetUseConsoleRunner() ) {
      if( wxFileExists( baseDir + strSLASH + strCONSOLE_RUNNER ) ) {
        command << crunnStr << strSPACE;
        command << CB_LIBRARY_ENVVAR << _T( "=$" ) << CB_LIBRARY_ENVVAR << _T( ':' );
        wxString strLinkerPath = cbGetDynamicLinkerPathForTarget( m_pProject, target );
        QuoteStringIfNeeded( strLinkerPath );
        command << strLinkerPath << strSPACE;
      }
    }
  }
  if( target->GetTargetType() == ttDynamicLib || target->GetTargetType() == ttStaticLib ) {
    if( target->GetHostApplication().IsEmpty() ) {
      msging( _T( "必须选择要“运行”库的主机应用程序..." ) );
      m_pProject->SetCurrentlyCompilingTarget( 0 );
      return -1;
    }
    Manager::Get()->GetMacrosManager()->ReplaceEnvVars( hostapStr );
    command << hostapStr << strSPACE;
    command << target->GetExecutionParameters();
  } else if( target->GetTargetType() != ttCommandsOnly ) {
    command << execStr << strSPACE;
    command << target->GetExecutionParameters();
    if( commandIsQuoted ) {
      command << strQUOTE;
    }
  } else {
    if( target->GetHostApplication().IsEmpty() ) {
      msging( _T( "您必须选择一个主机应用程序来“运行”一个仅用于命令的目标..." ) );
      m_pProject->SetCurrentlyCompilingTarget( 0 );
      return -1;
    }
    command << hostapStr << strSPACE;
    command << target->GetExecutionParameters();
    Manager::Get()->GetMacrosManager()->ReplaceMacros( command, target );
    Manager::Get()->GetMacrosManager()->ReplaceEnvVars( command );
  }
  wxString script = command;
  if( !cmd.Replace( _T( "$SCRIPT" ), script ) ) {
    cmd << command;
  }
  Manager::Get()->GetLogManager()->Log( _T( "检查是否存在: " ) + f.GetFullPath(), m_PageIndex );
  if( ( target->GetTargetType() != ttCommandsOnly ) && !wxFileExists( f.GetFullPath() ) ) {
    int ret = msging( _T( "这个项目似乎还没有建成.\n"
                          "Do you want to build it now?" ), _T( "Information" ), wxYES_NO | wxCANCEL | wxICON_QUESTION );
    switch( ret ) {
      case wxID_YES: {
        m_pProject->SetCurrentlyCompilingTarget( 0 );
        m_RunAfterCompile = true;
        Build( target );
        return -1;
      }
      case wxID_NO:
        break;
      default:
        m_pProject->SetCurrentlyCompilingTarget( 0 );
        return -1;
    }
  }
  Manager::Get()->GetLogManager()->Log( F( _T( "执行: %s (in %s)" ), cmd.wx_str(), m_CdRun.wx_str() ), m_PageIndex );
  m_CommandQueue.Add( new CompilerCommand( cmd, wxEmptyString, m_pProject, target, true ) );
  m_pProject->SetCurrentlyCompilingTarget( 0 );
  Manager::Get()->GetProjectManager()->SetIsRunning( this );
  return 0;
}

wxString CompilerGCC::GetMakeCommandFor( MakeCommand cmd, M_Code_Project* project, ProjectBuildTarget* target ) {
  if( !project ) {
    return wxEmptyString;
  }
  wxString compilerId = target ? target->GetCompilerID() : project->GetCompilerID();
  if( !CompilerFactory::IsValidCompilerID( compilerId ) ) {
    compilerId = CompilerFactory::GetDefaultCompilerID();
  }
  wxString command = target && !target->GetMakeCommandFor( cmd ).empty() ?
                     target->GetMakeCommandFor( cmd ) : project->GetMakeCommandFor( cmd );
  Compiler* compiler = CompilerFactory::GetCompiler( compilerId );
  command.Replace( _T( "$makefile" ), project->GetMakefile() );
  command.Replace( _T( "$make" ), compiler ? compiler->GetPrograms().MAKE : _T( "make" ) );
  command.Replace( _T( "$target" ), target ? target->GetTitle() : _T( "" ) );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( command );
  return command;
}

void CompilerGCC::DoClean( const wxArrayString& commands ) {
  for( unsigned int i = 0; i < commands.GetCount(); ++i ) {
    wxRemoveFile( commands[i] );
  }
}

int CompilerGCC::Clean( ProjectBuildTarget* target ) {
  return Clean( target ? target->GetTitle() : _T( "" ) );
}

int CompilerGCC::Clean( const wxString& target ) {
  m_LastBuildStep = true;
  return DoBuild( target, true, false );
}

static inline wxString getBuildTargetName( const ProjectBuildTarget *bt ) {
  return bt ? bt->GetTitle() : wxString( _T( "<all targets>" ) );
}

bool CompilerGCC::DoCleanWithMake( ProjectBuildTarget* bt ) {
  wxString cmd = GetMakeCommandFor( mcClean, m_pBuildingProject, bt );
  if( cmd.empty() ) {
    LogMessage( COMPILER_ERROR_LOG + _T( "“清除项目/目标”的make命令为空。什么都洗不干净!" ), cltError );
    return false;
  }
  Compiler* tgtCompiler = CompilerFactory::GetCompiler( bt->GetCompilerID() );
  if( !tgtCompiler ) {
    const wxString &message = F( _T( "选定目标的编译器无效 '%s'!" ), getBuildTargetName( bt ).wx_str() );
    LogMessage( COMPILER_ERROR_LOG + message, cltError );
    return false;
  }
  bool showOutput = ( tgtCompiler->GetSwitches().logging == clogFull );
  wxArrayString output, errors;
  wxSetWorkingDirectory( m_pBuildingProject->GetExecutionDir() );
  ExpandBackticks( cmd );
  if( showOutput ) {
    LogMessage( F( _T( "执行清除命令: %s" ), cmd.wx_str() ), cltNormal );
  }
  long result = wxExecute( cmd, output, errors, wxEXEC_SYNC );
  if( showOutput ) {
    for( size_t i = 0; i < output.GetCount(); i++ ) {
      LogMessage( F( _T( "%s" ), output[i].wx_str() ), cltNormal );
    }
    for( size_t i = 0; i < errors.GetCount(); i++ ) {
      LogMessage( F( _T( "%s" ), errors[i].wx_str() ), cltNormal );
    }
  }
  return ( result == 0 );
}

int CompilerGCC::DistClean( const wxString& target ) {
  if( !CheckProject() ) {
    return -1;
  }
  return DistClean( m_pProject->GetBuildTarget( target.IsEmpty() ? m_LastTargetName : target ) );
}

int CompilerGCC::DistClean( ProjectBuildTarget* target ) {
  if( m_pProject && !m_pProject->SaveAllFiles() ) {
    Manager::Get()->GetLogManager()->Log( _T( "无法保存所有文件..." ) );
  }
  if( !m_IsWorkspaceOperation ) {
    DoPrepareQueue( true );
  }
  if( !CompilerValid( target ).isValid ) {
    return -1;
  }
  if( m_pProject ) {
    wxSetWorkingDirectory( m_pProject->GetBasePath() );
  }
  if( UseMake() ) {
    wxString cmd = GetMakeCommandFor( mcDistClean, m_pProject, target );
    m_CommandQueue.Add( new CompilerCommand( cmd, wxEmptyString, m_pProject, target ) );
    return DoRunQueue();
  } else {
    NotImplemented( _T( "CompilerGCC::DistClean() without a custom Makefile" ) );
    return -1;
  }
  return 0;
}

void CompilerGCC::InitBuildState( BuildJob job, const wxString& target ) {
  m_BuildJob = job;
  m_BuildState = bsNone;
  m_NextBuildState = bsProjectPreBuild;
  m_pBuildingProject = 0;
  m_pLastBuildingProject = 0;
  m_pLastBuildingTarget = 0;
  m_BuildingTargetName = target;
  m_CommandQueue.Clear();
}

void CompilerGCC::ResetBuildState() {
  if( m_pBuildingProject ) {
    m_pBuildingProject->SetCurrentlyCompilingTarget( 0 );
  } else if( m_pProject ) {
    m_pProject->SetCurrentlyCompilingTarget( 0 );
  }
  m_BuildJob = bjIdle;
  m_BuildState = bsNone;
  m_NextBuildState = bsNone;
  m_pBuildingProject = 0;
  m_BuildingTargetName.Clear();
  m_pLastBuildingProject = 0;
  m_pLastBuildingTarget = 0;
  m_CommandQueue.Clear();
  ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
  for( size_t i = 0; i < arr->GetCount(); ++i ) {
    arr->Item( i )->SetCurrentlyCompilingTarget( 0 );
  }
}

inline wxString StateToString( BuildState bs ) {
  switch( bs ) {
    case bsNone:
      return _T( "bsNone" );
    case bsProjectPreBuild:
      return _T( "bsProjectPreBuild" );
    case bsTargetPreBuild:
      return _T( "bsTargetPreBuild" );
    case bsTargetClean:
      return _T( "bsTargetClean" );
    case bsTargetBuild:
      return _T( "bsTargetBuild" );
    case bsTargetPostBuild:
      return _T( "bsTargetPostBuild" );
    case bsTargetDone:
      return _T( "bsTargetDone" );
    case bsProjectPostBuild:
      return _T( "bsProjectPostBuild" );
    case bsProjectDone:
      return _T( "bsProjectDone" );
    default:
      break;
  }
  return _T( "Huh!?!" );
}

BuildState CompilerGCC::GetNextStateBasedOnJob() {
  bool clean = m_Clean;
  bool build = m_Build;
  switch( m_BuildState ) {
    case bsProjectPreBuild: {
      if( clean && !build ) {
        return bsTargetClean;
      }
      return bsTargetPreBuild;
    }
    case bsTargetPreBuild: {
      if( clean ) {
        return bsTargetClean;
      } else if( build ) {
        return bsTargetBuild;
      }
      return bsTargetPostBuild;
    }
    case bsTargetClean: {
      if( build ) {
        return bsTargetBuild;
      }
      return bsTargetDone;
    }
    case bsTargetBuild:
      return bsTargetPostBuild;
    case bsTargetPostBuild:
      return bsTargetDone;
    case bsTargetDone: {
      if( m_BuildJob != bjTarget ) {
        const BuildJobTarget& bj = PeekNextJob();
        if( bj.project && bj.project == m_pBuildingProject ) {
          m_BuildingTargetName = bj.targetName;
          GetNextJob();
          if( clean && !build ) {
            return bsTargetClean;
          }
          return bsTargetPreBuild;
        }
        if( build ) {
          return bsProjectPostBuild;
        }
        return bsProjectDone;
      }
      m_pBuildingProject->SetCurrentlyCompilingTarget( 0 );
      break;
    }
    case bsProjectPostBuild:
      return bsProjectDone;
    case bsProjectDone: {
      if( m_pBuildingProject ) {
        m_pBuildingProject->SetCurrentlyCompilingTarget( 0 );
      }
      m_NextBuildState = bsProjectPreBuild;
      if( DoBuild( clean, build ) >= 0 ) {
        if( clean && !build ) {
          return bsTargetClean;
        }
        return bsTargetPreBuild;
      } else
      { return bsNone; }
    }
    case bsNone:
    default:
      break;
  }
  return bsNone;
}

void CompilerGCC::BuildStateManagement() {
  if( IsProcessRunning() ) {
    return;
  }
  Manager::Yield();
  if( !m_pBuildingProject ) {
    ResetBuildState();
    return;
  }
  ProjectBuildTarget* bt = m_pBuildingProject->GetBuildTarget(
                             GetTargetIndexFromName( m_pBuildingProject, m_BuildingTargetName ) );
  if( !bt ) {
    ResetBuildState();
    return;
  }
  if( m_pBuildingProject != m_pLastBuildingProject || bt != m_pLastBuildingTarget ) {
    Manager::Get()->GetMacrosManager()->RecalcVars( m_pBuildingProject,
        Manager::Get()->GetEditorManager()->GetActiveEditor(), bt );
    if( bt ) {
      SwitchCompiler( bt->GetCompilerID() );
    }
    if( m_pBuildingProject != m_pLastBuildingProject ) {
      m_pLastBuildingProject = m_pBuildingProject;
      wxSetWorkingDirectory( m_pBuildingProject->GetBasePath() );
    }
    if( bt != m_pLastBuildingTarget ) {
      m_pLastBuildingTarget = bt;
    }
  }
  m_pBuildingProject->SetCurrentlyCompilingTarget( bt );
  DirectCommands dc( this, CompilerFactory::GetCompiler( bt->GetCompilerID() ), m_pBuildingProject, m_PageIndex );
  dc.m_doYield = true;
  m_BuildState = m_NextBuildState;
  wxArrayString cmds;
  switch( m_NextBuildState ) {
    case bsProjectPreBuild: {
      if( m_Build ) {
        cmds = dc.GetPreBuildCommands( 0 );
      }
      break;
    }
    case bsTargetPreBuild: {
      cmds = dc.GetPreBuildCommands( bt );
      break;
    }
    case bsTargetClean: {
      PrintBanner( baClean, m_pBuildingProject, bt );
      bool result;
      if( UseMake( m_pBuildingProject ) ) {
        result = DoCleanWithMake( bt );
      } else {
        wxArrayString clean = dc.GetCleanCommands( bt, true );
        DoClean( clean );
        result = true;
      }
      if( result ) {
        const wxString &message = F( _T( "清理 \"%s - %s\"" ), m_pBuildingProject->GetTitle().wx_str(),
                                     getBuildTargetName( bt ).wx_str() );
        LogMessage( message, cltNormal );
      } else {
        const wxString &message = F( _T( "错误清除\"%s - %s\"" ), m_pBuildingProject->GetTitle().wx_str(),
                                     getBuildTargetName( bt ).wx_str() );
        LogMessage( COMPILER_ERROR_LOG + message, cltError );
      }
      break;
    }
    case bsTargetBuild: {
      PrintBanner( baBuild, m_pBuildingProject, bt );
      if( UseMake( m_pBuildingProject ) ) {
        wxArrayString output, error;
        wxSetWorkingDirectory( m_pBuildingProject->GetExecutionDir() );
        const wxString &askCmd = GetMakeCommandFor( mcAskRebuildNeeded, m_pBuildingProject, bt );
        Compiler* tgtCompiler = CompilerFactory::GetCompiler( bt->GetCompilerID() );
        bool runMake = false;
        if( !askCmd.empty() ) {
          if( tgtCompiler && tgtCompiler->GetSwitches().logging == clogFull ) {
            cmds.Add( COMPILER_SIMPLE_LOG + _T( "检查目标是否是最新的: " ) + askCmd );
          }
          runMake = ( wxExecute( askCmd, output, error, wxEXEC_SYNC | wxEXEC_NODISABLE ) != 0 );
        } else {
          cmds.Add( COMPILER_SIMPLE_LOG + _T( "询问是否需要重新生成的命令为空。假设需要重建!" ) );
          runMake = true;
        }
        if( runMake && tgtCompiler ) {
          bool isEmpty = false;
          switch( tgtCompiler->GetSwitches().logging ) {
            case clogFull: {
              const wxString &cmd = GetMakeCommandFor( mcBuild, m_pBuildingProject, bt );
              if( !cmd.empty() ) {
                cmds.Add( COMPILER_SIMPLE_LOG + _T( "运行命令: " ) + cmd );
                cmds.Add( cmd );
              } else
              { isEmpty = true; }
              break;
            }
            case clogSimple:
              cmds.Add( COMPILER_SIMPLE_LOG + _T( "使用make: " ) + m_pBuildingProject->GetMakefile() );
            case clogNone: {
              const wxString &cmd = GetMakeCommandFor( mcSilentBuild, m_pBuildingProject, bt );
              if( !cmd.empty() )
              { cmds.Add( cmd ); }
              else
              { isEmpty = true; }
              break;
            }
            default:
              break;
          }
          if( isEmpty ) {
            cmds.Add( COMPILER_ERROR_LOG + _T( "生成目标的make命令为空!" ) );
          }
        }
      } else
      { cmds = dc.GetCompileCommands( bt ); }
      bool hasCommands = cmds.GetCount();
      m_RunTargetPostBuild = hasCommands;
      m_RunProjectPostBuild = hasCommands;
      if( !hasCommands ) {
        LogMessage( _T( "目标是最新的." ) );
      }
      break;
    }
    case bsTargetPostBuild: {
      if( m_RunTargetPostBuild || bt->GetAlwaysRunPostBuildSteps() ) {
        cmds = dc.GetPostBuildCommands( bt );
      }
      m_RunTargetPostBuild = false;
      break;
    }
    case bsProjectPostBuild: {
      if( m_RunProjectPostBuild || m_pBuildingProject->GetAlwaysRunPostBuildSteps() ) {
        cmds = dc.GetPostBuildCommands( 0 );
      }
      m_pLastBuildingTarget = 0;
      m_RunProjectPostBuild = false;
      break;
    }
    case bsProjectDone: {
      m_pLastBuildingProject = 0;
      break;
    }
    case bsTargetDone:
    case bsNone:
    default:
      break;
  }
  m_NextBuildState = GetNextStateBasedOnJob();
  AddToCommandQueue( cmds );
  Manager::Yield();
}

int CompilerGCC::GetTargetIndexFromName( M_Code_Project* prj, const wxString& name ) {
  if( !prj || name.IsEmpty() ) {
    return -1;
  }
  for( int i = 0; i < prj->GetBuildTargetsCount(); ++i ) {
    ProjectBuildTarget* bt_search =  prj->GetBuildTarget( i );
    if( bt_search->GetTitle() == name ) {
      return i;
    }
  }
  return -1;
}

void CompilerGCC::ExpandTargets( M_Code_Project* project, const wxString& targetName, wxArrayString& result ) {
  result.Clear();
  if( project ) {
    ProjectBuildTarget* bt =  project->GetBuildTarget( targetName );
    if( bt ) {
      result.Add( targetName );
    } else
    { result = project->GetExpandedVirtualBuildTargetGroup( targetName ); }
  }
}

void CompilerGCC::PreprocessJob( M_Code_Project* project, const wxString& targetName ) {
  wxArrayString tlist;
  if( !m_IsWorkspaceOperation ) {
    while( !m_BuildJobTargetsList.empty() ) {
      m_BuildJobTargetsList.pop();
    }
  }
  wxArrayInt deps;
  if( !project ) {
    CalculateWorkspaceDependencies( deps );
  } else {
    CalculateProjectDependencies( project, deps );
  }
  for( size_t i = 0; i < deps.GetCount(); ++i ) {
    M_Code_Project* prj = Manager::Get()->GetProjectManager()->GetProjects()->Item( deps[i] );
    ExpandTargets( prj, targetName, tlist );
    if( tlist.GetCount() == 0 ) {
      Manager::Get()->GetLogManager()->LogWarning( F( _T( "警告：没有名为的目标 '%s' 在项目中 '%s'. 已忽略..." ),
          targetName.wx_str(), prj->GetTitle().wx_str() ) );
    }
    for( size_t x = 0; x < tlist.GetCount(); ++x ) {
      ProjectBuildTarget* tgt = prj->GetBuildTarget( tlist[x] );
      CompilerValidResult result = CompilerValid( tgt );
      if( !result.isValid ) {
        PrintInvalidCompiler( tgt, result.compiler, _T( "已忽略..." ) );
        continue;
      }
      BuildJobTarget bjt;
      bjt.project = prj;
      bjt.targetName = tlist[x];
      m_BuildJobTargetsList.push( bjt );
    }
  }
  if( m_BuildJobTargetsList.empty() ) {
    NotifyJobDone( true );
  }
}

CompilerGCC::BuildJobTarget CompilerGCC::GetNextJob() {
  BuildJobTarget ret;
  if( m_BuildJobTargetsList.empty() ) {
    return ret;
  }
  ret = m_BuildJobTargetsList.front();
  m_BuildJobTargetsList.pop();
  return ret;
}

const CompilerGCC::BuildJobTarget& CompilerGCC::PeekNextJob() {
  static BuildJobTarget ret;
  if( m_BuildJobTargetsList.empty() ) {
    return ret;
  }
  return m_BuildJobTargetsList.front();
}

int CompilerGCC::DoBuild( bool clean, bool build ) {
  BuildJobTarget bj = GetNextJob();
  if( !bj.project ) {
    return -2;
  }
  if( bj.project && ( bj.project != m_pBuildingProject ) && !bj.project->SaveAllFiles() ) {
    Manager::Get()->GetLogManager()->LogWarning( _T( "无法保存所有文件..." ) );
  }
  m_pBuildingProject = bj.project;
  m_BuildingTargetName = bj.targetName;
  ProjectBuildTarget* bt = bj.project->GetBuildTarget( bj.targetName );
  m_Clean = clean;
  m_Build = build;
  if( !bt || !CompilerValid( bt ).isValid ) {
    return -2;
  }
  BuildStateManagement();
  return 0;
}

void CompilerGCC::CalculateWorkspaceDependencies( wxArrayInt& deps ) {
  deps.Clear();
  ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
  for( size_t i = 0; i < arr->GetCount(); ++i ) {
    CalculateProjectDependencies( arr->Item( i ), deps );
  }
}

void CompilerGCC::CalculateProjectDependencies( M_Code_Project* prj, wxArrayInt& deps ) {
  int prjidx = Manager::Get()->GetProjectManager()->GetProjects()->Index( prj );
  const ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetDependenciesForProject( prj );
  if( !arr || !arr->GetCount() ) {
    if( deps.Index( prjidx ) == wxNOT_FOUND ) {
      deps.Add( prjidx );
    }
    return;
  }
  for( size_t i = 0; i < arr->GetCount(); ++i ) {
    M_Code_Project* thisprj = arr->Item( i );
    if( !Manager::Get()->GetProjectManager()->CausesCircularDependency( prj, thisprj ) ) {
      CalculateProjectDependencies( thisprj, deps );
      ProjectsArray* parr = Manager::Get()->GetProjectManager()->GetProjects();
      int idx = parr->Index( thisprj );
      if( idx != wxNOT_FOUND ) {
        if( deps.Index( idx ) == wxNOT_FOUND ) {
          deps.Add( idx );
        }
      }
    } else {
      Manager::Get()->GetLogManager()->Log( F( _T( "检测到循环依赖项 \"%s\" 和 \"%s\". 已跳过..." ), prj->GetTitle().wx_str(),
                                            thisprj->GetTitle().wx_str() ), m_PageIndex, Logger::warning );
    }
  }
  if( deps.Index( prjidx ) == wxNOT_FOUND ) {
    deps.Add( prjidx );
  }
}

int CompilerGCC::DoBuild( const wxString& target, bool clean, bool build, bool clearLog ) {
  wxString realTarget = target;
  if( realTarget.IsEmpty() ) {
    realTarget = GetTargetString();
  }
  if( !StopRunningDebugger() ) {
    return -1;
  }
  if( !CheckProject() ) {
    if( Manager::Get()->GetEditorManager()->GetActiveEditor() ) {
      return CompileFile( Manager::Get()->GetEditorManager()->GetActiveEditor()->GetFilename() );
    }
    return -1;
  }
  if( realTarget.IsEmpty() ) {
    return -1;
  }
  if( !m_IsWorkspaceOperation ) {
    DoClearErrors();
    InitBuildLog( false );
    DoPrepareQueue( clearLog );
    if( clean ) {
      NotifyCleanProject( realTarget );
    }
  }
  PreprocessJob( m_pProject, realTarget );
  if( m_BuildJobTargetsList.empty() ) {
    return -1;
  }
  InitBuildState( bjProject, realTarget );
  if( DoBuild( clean, build ) ) {
    return -2;
  }
  return DoRunQueue();
}

int CompilerGCC::Build( const wxString& target ) {
  m_LastBuildStep = true;
  return DoBuild( target, false, true );
}

int CompilerGCC::Build( ProjectBuildTarget* target ) {
  return Build( target ? target->GetTitle() : _T( "" ) );
}

int CompilerGCC::Rebuild( ProjectBuildTarget* target ) {
  return Rebuild( target ? target->GetTitle() : _T( "" ) );
}

int CompilerGCC::Rebuild( const wxString& target ) {
  m_LastBuildStep = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/rebuild_seperately" ), false );
  if( m_LastBuildStep ) {
    return DoBuild( target, true, true );
  }
  int result = DoBuild( target, true, false );
  m_LastBuildStep = true;
  return result + DoBuild( target, false, true, false );
}

int CompilerGCC::DoWorkspaceBuild( const wxString& target, bool clean, bool build, bool clearLog ) {
  wxString realTarget = target;
  if( realTarget.IsEmpty() ) {
    realTarget = GetTargetString();
  }
  if( realTarget.IsEmpty() ) {
    return -1;
  }
  if( !StopRunningDebugger() ) {
    return -1;
  }
  DoPrepareQueue( clearLog );
  if( clean ) {
    NotifyCleanWorkspace();
  }
  m_IsWorkspaceOperation = true;
  InitBuildLog( true );
  ProjectsArray* arr = Manager::Get()->GetProjectManager()->GetProjects();
  if( arr ) {
    for( size_t i = 0; i < arr->GetCount(); ++i ) {
      M_Code_Project* prj = arr->Item( i );
      if( prj && !prj->SaveAllFiles() ) {
        Manager::Get()->GetLogManager()->Log( F( _T( "无法保存的所有文件 %s..." ), prj->GetTitle().wx_str() ), m_PageIndex );
      }
    }
  }
  PreprocessJob( 0, realTarget );
  if( m_BuildJobTargetsList.empty() ) {
    return -1;
  }
  InitBuildState( bjWorkspace, realTarget );
  DoBuild( clean, build );
  m_IsWorkspaceOperation = false;
  return DoRunQueue();
}

int CompilerGCC::BuildWorkspace( const wxString& target ) {
  return DoWorkspaceBuild( target, false, true );
}

int CompilerGCC::RebuildWorkspace( const wxString& target ) {
  m_LastBuildStep = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/rebuild_seperately" ), false );
  if( m_LastBuildStep ) {
    return DoWorkspaceBuild( target, true, true );
  }
  int result = DoWorkspaceBuild( target, true, false );
  m_LastBuildStep = true;
  return result + DoWorkspaceBuild( target, false, true, false );
}

int CompilerGCC::CleanWorkspace( const wxString& target ) {
  return DoWorkspaceBuild( target, true, false );
}

int CompilerGCC::KillProcess() {
  ResetBuildState();
  m_RunAfterCompile = false;
  if( !IsProcessRunning() ) {
    return 0;
  }
  if( !m_CommandQueue.LastCommandWasRun() ) {
    LogMessage( _T( "中止构建..." ), cltInfo, ltMessages );
  }
  wxKillError ret = wxKILL_OK;
  m_CommandQueue.Clear();
  for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
    if( !m_CompilerProcessList.at( i ).pProcess ) {
      continue;
    }
    m_CompilerProcessList.at( i ).pProcess->CloseOutput();
    ( ( PipedProcess* ) m_CompilerProcessList.at( i ).pProcess )->ForfeitStreams();
    ret = wxProcess::Kill( m_CompilerProcessList.at( i ).PID, wxSIGTERM );
    if( ret != wxKILL_OK ) {
      Manager::Get()->GetLogManager()->Log( F( _T( "中止进程 %d ..." ), i ), m_PageIndex );
    } else
      switch( ret ) {
        case wxKILL_OK:
          Manager::Get()->GetLogManager()->Log( _T( "进程中止（终止）." ), m_PageIndex );
        case wxKILL_ACCESS_DENIED:
        case wxKILL_NO_PROCESS:
        case wxKILL_BAD_SIGNAL:
        case wxKILL_ERROR:
        default:
          break;
      }
  }
  ProjectManager *projectManager = Manager::Get()->GetProjectManager();
  if( projectManager->GetIsRunning() == this ) {
    projectManager->SetIsRunning( NULL );
  }
  return ret;
}

bool CompilerGCC::IsRunning() const {
  return m_BuildJob != bjIdle || IsProcessRunning() || m_CommandQueue.GetCount();
}

ProjectBuildTarget* CompilerGCC::GetBuildTargetForFile( ProjectFile* pf ) {
  if( !pf ) {
    return nullptr;
  }
  if( !pf->buildTargets.GetCount() ) {
    InfoWindow::Tip( _T( "该文件未分配给任何目标" ), _T( "错误" ), 3500 );
    return nullptr;
  }
  if( m_RealTargetIndex == -1 ) {
    int idx = DoGUIAskForTarget();
    if( idx == -1 ) {
      return nullptr;
    }
    return m_pProject->GetBuildTarget( idx );
  }
  const wxString &targetName = m_Targets[m_TargetIndex];
  if( std::find( pf->buildTargets.begin(), pf->buildTargets.end(), targetName ) == pf->buildTargets.end() ) {
    return nullptr;
  }
  return m_pProject->GetBuildTarget( targetName );
}

int CompilerGCC::CompileFile( const wxString& file ) {
  CheckProject();
  DoClearErrors();
  DoPrepareQueue( false );
  ProjectFile* pf = m_pProject ? m_pProject->GetFileByFilename( file, true, false ) : 0;
  ProjectBuildTarget* bt = GetBuildTargetForFile( pf );
  PrintBanner( baBuildFile, m_pProject, bt );
  if( !CompilerValid( bt ).isValid ) {
    return -1;
  }
  if( !pf ) {
    return CompileFileWithoutProject( file );
  }
  if( !bt ) {
    const wxString err( _T( "错误：找不到文件的目标" ) );
    LogMessage( pf->relativeToCommonTopLevelPath + _T( ": " ) + err, cltError );
    LogWarningOrError( cltError, m_pProject, pf->relativeToCommonTopLevelPath, wxEmptyString, err );
    return -2;
  }
  if( m_pProject ) {
    wxSetWorkingDirectory( m_pProject->GetBasePath() );
  }
  return CompileFileDefault( m_pProject, pf, bt );
}

int CompilerGCC::CompileFileWithoutProject( const wxString& file ) {
  Manager::Get()->GetEditorManager()->Save( file );
  SwitchCompiler( CompilerFactory::GetDefaultCompilerID() );
  Manager::Get()->GetMacrosManager()->Reset();
  Compiler* compiler = CompilerFactory::GetDefaultCompiler();
  DirectCommands dc( this, compiler, 0, m_PageIndex );
  wxArrayString compile = dc.GetCompileSingleFileCommand( file );
  AddToCommandQueue( compile );
  return DoRunQueue();
}

int CompilerGCC::CompileFileDefault( M_Code_Project* project, ProjectFile* pf, ProjectBuildTarget* bt ) {
  Compiler* compiler = CompilerFactory::GetCompiler( bt->GetCompilerID() );
  if( !compiler ) {
    const wxString &err = F( _T( "错误：无法为目标生成文件 '%s'. 编译器 '%s' 找不到!" ),
                             bt->GetTitle().wx_str(), bt->GetCompilerID().wx_str() );
    LogMessage( pf->relativeToCommonTopLevelPath + _T( ": " ) + err, cltError );
    LogWarningOrError( cltError, project, pf->relativeToCommonTopLevelPath, wxEmptyString, err );
    return -3;
  }
  DirectCommands dc( this, compiler, project, m_PageIndex );
  wxArrayString compile = dc.CompileFile( bt, pf );
  AddToCommandQueue( compile );
  return DoRunQueue();
}


void CompilerGCC::OnIdle( wxIdleEvent& event ) {
  if( IsProcessRunning() ) {
    for( size_t i = 0; i < m_CompilerProcessList.size(); ++i ) {
      if( ( m_CompilerProcessList.at( i ).pProcess != 0 )
          && ( static_cast<PipedProcess*>( m_CompilerProcessList.at( i ).pProcess ) )->HasInput() ) {
        event.RequestMore();
        break;
      }
    }
  } else {
    event.Skip();
  }
}

void CompilerGCC::OnTimer( M_Code_unused wxTimerEvent& event ) {
  wxWakeUpIdle();
}

void CompilerGCC::OnRun( M_Code_unused wxCommandEvent& event ) {
  if( Run() == 0 ) {
    DoRunQueue();
  }
}

void CompilerGCC::OnCompileAndRun( M_Code_unused wxCommandEvent& event ) {
  ProjectBuildTarget* target = 0;
  m_RunAfterCompile = true;
  Build( target );
}

void CompilerGCC::OnCompile( wxCommandEvent& event ) {
  int bak = m_RealTargetIndex;
  if( event.GetId() == idMenuCompileFromProjectManager ) {
    DoSwitchProjectTemporarily();
  }
  ProjectBuildTarget* target = 0;
  Build( target );
  m_RealTargetIndex = bak;
}

void CompilerGCC::OnCompileFile( wxCommandEvent& event ) {
  wxFileName file;
  if( event.GetId() == idMenuCompileFileFromProjectManager ) {
    PrepareCompileFilePM( file );
  } else {
    PrepareCompileFile( file );
  }
  StartCompileFile( file );
}

void CompilerGCC::OnCleanFile( wxCommandEvent& event ) {
  if( event.GetId() == idMenuCleanFileFromProjectManager ) {
    FileTreeData* ftd = DoSwitchProjectTemporarily();
    ProjectFile* pf = ftd->GetProjectFile();
    if( !pf ) {
      return;
    }
    ProjectBuildTarget* bt = GetBuildTargetForFile( pf );
    if( !bt ) {
      return;
    }
    Compiler* compiler = CompilerFactory::GetCompiler( bt->GetCompilerID() );
    if( !compiler ) {
      return;
    }
    if( !CheckProject() ) {
      return;
    }
    wxSetWorkingDirectory( m_pProject->GetBasePath() );
    wxFileName fn( pf->GetObjName() );
    wxString obj_name = ( compiler->GetSwitches().UseFlatObjects ) ? fn.GetFullName() : fn.GetFullPath();
    wxString obj_file = wxFileName( bt->GetObjectOutput() + _T( '/' ) + obj_name ).GetFullPath();
    Manager::Get()->GetMacrosManager()->ReplaceEnvVars( obj_file );
    wxFileName obj_fn( obj_file );
    if( obj_fn.FileExists() ) {
      if( wxRemoveFile( obj_file ) ) {
        Manager::Get()->GetLogManager()->Log( F( _T( "文件已被删除: %s" ), obj_file.wx_str() ) );
      } else
      { Manager::Get()->GetLogManager()->Log( F( _T( "删除文件失败: %s" ), obj_file.wx_str() ) ); }
    } else
    { Manager::Get()->GetLogManager()->Log( F( _T( "要删除的文件不存在: %s" ), obj_file.wx_str() ) ); }
  }
}

void CompilerGCC::OnRebuild( wxCommandEvent& event ) {
  CheckProject();
  int bak = m_RealTargetIndex;
  if( event.GetId() == idMenuRebuildFromProjectManager ) {
    DoSwitchProjectTemporarily();
  }
  ProjectBuildTarget* target = 0;
  Rebuild( target );
  m_RealTargetIndex = bak;
}

void CompilerGCC::OnCompileAll( M_Code_unused wxCommandEvent& event ) {
  BuildWorkspace();
}

void CompilerGCC::OnRebuildAll( M_Code_unused wxCommandEvent& event ) {
  RebuildWorkspace();
}

void CompilerGCC::OnCleanAll( M_Code_unused wxCommandEvent& event ) {
  CleanWorkspace();
}

void CompilerGCC::OnClean( wxCommandEvent& event ) {
  CheckProject();
  int bak = m_RealTargetIndex;
  if( event.GetId() == idMenuCleanFromProjectManager ) {
    DoSwitchProjectTemporarily();
  }
  ProjectBuildTarget* target = 0;
  Clean( target );
  m_RealTargetIndex = bak;
}

void CompilerGCC::OnProjectCompilerOptions( M_Code_unused wxCommandEvent& event ) {
  ProjectManager* manager = Manager::Get()->GetProjectManager();
  wxTreeCtrl* tree = manager->GetUI().GetTree();
  wxTreeItemId sel = manager->GetUI().GetTreeSelection();
  FileTreeData* ftd = sel.IsOk() ? ( FileTreeData* )tree->GetItemData( sel ) : 0;
  if( ftd ) {
    ProjectBuildTarget* target = 0;
    M_Code_Project *currentProject = ftd->GetProject();
    if( currentProject == m_pProject ) {
      if( m_RealTargetIndex != -1 ) {
        target = m_pProject->GetBuildTarget( m_RealTargetIndex );
      }
    } else if( m_RealTargetIndex != -1 && m_pProject ) {
      ProjectBuildTarget *activeTarget = m_pProject->GetBuildTarget( m_RealTargetIndex );
      if( activeTarget ) {
        target = currentProject->GetBuildTarget( activeTarget->GetTitle() );
      }
    }
    Configure( currentProject, target );
  } else {
    if( M_Code_Project* prj = Manager::Get()->GetProjectManager()->GetActiveProject() ) {
      Configure( prj );
    }
  }
}

void CompilerGCC::OnTargetCompilerOptions( M_Code_unused wxCommandEvent& event ) {
  int bak = m_RealTargetIndex;
  int idx = DoGUIAskForTarget();
  if( idx == -1 ) {
    return;
  } else {
    m_RealTargetIndex = idx;
  }
  DoSwitchProjectTemporarily();
  ProjectBuildTarget* target = 0;
  m_RealTargetIndex = bak;
  Configure( m_pProject, target );
}

void CompilerGCC::OnKillProcess( M_Code_unused wxCommandEvent& event ) {
  KillProcess();
}

void CompilerGCC::OnSelectTarget( wxCommandEvent& event ) {
  int selection = -1;
  bool updateTools = false;
  if( event.GetId() == idToolTarget ) {
    selection = event.GetSelection();
  } else if( event.GetId() == idMenuSelectTargetDialog ) {
    IncrementalSelectArrayIterator iterator( m_Targets );
    IncrementalSelectDialog dlg( Manager::Get()->GetAppWindow(), &iterator, _T( "选择目标..." ), _T( "搜索目标:" ) );
    if( dlg.ShowModal() == wxID_OK ) {
      selection = dlg.GetSelection();
      updateTools = true;
    }
  } else {
    selection = event.GetId() - idMenuSelectTargetOther[0];
    updateTools = true;
  }
  if( selection >= 0 ) {
    Manager::Get()->GetProjectManager()->GetWorkspace()->SetPreferredTarget( GetTargetString( selection ) );
    DoUpdateTargetMenu( selection );
    if( updateTools ) {
      m_pToolTarget->SetSelection( selection );
    }
  }
}

void CompilerGCC::OnNextError( M_Code_unused wxCommandEvent& event ) {
  DoGotoNextError();
}

void CompilerGCC::OnPreviousError( M_Code_unused wxCommandEvent& event ) {
  DoGotoPreviousError();
}

void CompilerGCC::OnClearErrors( M_Code_unused wxCommandEvent& event ) {
  DoClearErrors();
}

void CompilerGCC::OnUpdateUI( wxUpdateUIEvent& event ) {
  M_Code_Project* prj = Manager::Get()->GetProjectManager()->GetActiveProject();
  M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinActiveEditor();
  wxMenuBar* mbar = Manager::Get()->GetAppFrame()->GetMenuBar();
  bool running = IsRunning();
  M_Code_Plugin *runningPlugin = Manager::Get()->GetProjectManager()->GetIsRunning();
  bool otherRunning = runningPlugin && runningPlugin != this;
  if( mbar ) {
    mbar->Enable( idMenuCompile, !running && ( prj || ed ) && !otherRunning );
    mbar->Enable( idMenuBuildWorkspace, !running && prj && !otherRunning );
    mbar->Enable( idMenuCompileFile, !running && ed && !otherRunning );
    mbar->Enable( idMenuRebuild, !running && prj && !otherRunning );
    mbar->Enable( idMenuRebuildWorkspace, !running && prj && !otherRunning );
    mbar->Enable( idMenuClean, !running && prj && !otherRunning );
    mbar->Enable( idMenuCleanWorkspace, !running && prj && !otherRunning );
    mbar->Enable( idMenuCompileAndRun, !running && ( prj || ed ) && !otherRunning );
    mbar->Enable( idMenuRun, !running && ( prj || ed ) && !otherRunning );
    mbar->Enable( idMenuKillProcess, running );
    mbar->Enable( idMenuSelectTarget, !running && prj && !otherRunning );
    mbar->Enable( idMenuNextError, !running && ( prj || ed ) && m_Errors.HasNextError() && !otherRunning );
    mbar->Enable( idMenuPreviousError, !running && ( prj || ed ) && m_Errors.HasPreviousError() && !otherRunning );
    mbar->Enable( idMenuClearErrors, !otherRunning );
    mbar->Enable( idMenuProjectCompilerOptions, !running && prj && !otherRunning );
  }
  wxToolBar* tbar = m_pTbar;
  if( tbar ) {
    tbar->EnableTool( idMenuCompile, !running && ( prj || ed ) && !otherRunning );
    tbar->EnableTool( idMenuRun,    !running && ( prj || ed ) && !otherRunning );
    tbar->EnableTool( idMenuCompileAndRun, !running && ( prj || ed ) && !otherRunning );
    tbar->EnableTool( idMenuRebuild, !running && prj         && !otherRunning );
    tbar->EnableTool( idMenuKillProcess, running && prj );
    tbar->EnableTool( idMenuSelectTargetDialog, !running && prj && !otherRunning );
    //m_pToolTarget = XRCCTRL( *tbar, "idToolTarget", wxChoice );
    if( m_pToolTarget ) {
      m_pToolTarget->Enable( !running && prj && !otherRunning );
    }
  }
  event.Skip();
}

void CompilerGCC::OnProjectActivated( CodeBlocksEvent& event ) {
  M_Code_Project* active = Manager::Get()->GetProjectManager()->GetActiveProject();
  if( event.GetProject() == active ) {
    UpdateProjectTargets( event.GetProject() );
  }
}

void CompilerGCC::OnProjectLoaded( M_Code_unused CodeBlocksEvent& event ) {
}

void CompilerGCC::OnProjectUnloaded( CodeBlocksEvent& event ) {
  if( m_pProject == event.GetProject() ) {
    m_pProject = 0;
  }
}

void CompilerGCC::OnCompileFileRequest( CodeBlocksEvent& event ) {
  M_Code_Project*  prj = event.GetProject();
  EditorBase* eb  = event.GetEditor();
  if( !prj || !eb ) {
    return;
  }
  const wxString& ed_filename = eb->GetFilename();
  wxFileName wx_filename;
  wx_filename.Assign( ed_filename );
  wx_filename.MakeRelativeTo( prj->GetBasePath() );
  wxString filepath = wx_filename.GetFullPath();
  if( filepath.IsEmpty() ) {
    return;
  }
  ProjectFile* pf = prj->GetFileByFilename( UnixFilename( filepath ), true, false );
  if( !pf || !pf->buildTargets.GetCount() ) {
    return;
  }
  ProjectBuildTarget* bt = 0;
  if( pf->buildTargets.GetCount() == 1 ) {
    bt = prj->GetBuildTarget( pf->buildTargets[0] );
  } else {
    bt = prj->GetBuildTarget( m_RealTargetIndex );
  }
  if( !bt ) {
    return;
  }
  Manager::Get()->GetLogManager()->Log( F( _T( "正在为执行传入的编译文件请求 '%s'." ), filepath.wx_str() ) );
  CompileFileDefault( prj, pf, bt );
}

void CompilerGCC::OnGCCOutput( CodeBlocksEvent& event ) {
  wxString msg = event.GetString();
  if( !msg.IsEmpty() && !msg.Matches( _T( "# ??*" ) ) ) {
    AddOutputLine( msg );
  }
}

void CompilerGCC::OnGCCError( CodeBlocksEvent& event ) {
  wxString msg = event.GetString();
  if( !msg.IsEmpty() ) {
    AddOutputLine( msg );
  }
}

void CompilerGCC::OnGCCTerminated( CodeBlocksEvent& event ) {
  const int index = event.GetX();
  OnJobEnd( index, event.GetInt() );
}

void CompilerGCC::AddOutputLine( const wxString& output, bool forceErrorColour ) {
  wxArrayString ignore_output = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadArrayString( _T( "/ignore_output" ) );
  if( !ignore_output.IsEmpty() ) {
    for( size_t i = 0; i < ignore_output.GetCount(); ++i ) {
      if( output.Find( ignore_output.Item( i ) ) != wxNOT_FOUND ) {
        Manager::Get()->GetLogManager()->Log( F( _T( "忽略编译器输出: %s" ), output.wx_str() ) );
        return;
      }
    }
  }
  Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
  if( !compiler ) {
    return;
  }
  CompilerLineType clt = compiler->CheckForWarningsAndErrors( output );
  size_t maxErrors = Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadInt( _T( "/max_reported_errors" ), 50 );
  if( maxErrors > 0 && m_Errors.GetCount( cltError ) == maxErrors ) {
    LogMessage( output, clt, ltFile, forceErrorColour );
    if( !m_NotifiedMaxErrors ) {
      m_NotifiedMaxErrors = true;
      LogWarningOrError( cltNormal, 0, wxEmptyString, wxEmptyString, _T( "超出最大错误限制 以忽略更多错误输出" ) );
    }
    return;
  }
  if( clt != cltNormal ) {
    wxString last_error_filename = compiler->GetLastErrorFilename();
    if( UseMake() ) {
      wxFileName last_error_file( last_error_filename );
      if( !last_error_file.IsAbsolute() ) {
        M_Code_Project* project = m_pProject;
        if( m_pLastBuildingTarget ) {
          project = m_pLastBuildingTarget->GetParentProject();
        } else {
          AskForActiveProject();
          project = m_pProject;
        }
        last_error_file = project->GetExecutionDir() + wxFileName::GetPathSeparator() + last_error_file.GetFullPath();
        last_error_file.MakeRelativeTo( project->GetBasePath() );
        last_error_filename = last_error_file.GetFullPath();
      }
    }
    wxString msg = compiler->GetLastError();
    if( !compiler->WithMultiLineMsg() || ( compiler->WithMultiLineMsg() && !msg.IsEmpty() ) )
      if( !compiler->GetLastErrorLine().IsEmpty() ) {
        LogWarningOrError( clt, m_pBuildingProject, last_error_filename, compiler->GetLastErrorLine(), msg );
      }
  }
  LogMessage( output, clt, ltAll, forceErrorColour );
}

void CompilerGCC::LogWarningOrError( CompilerLineType lt, M_Code_Project* prj, const wxString& filename, const wxString& line, const wxString& msg ) {
  wxArrayString errors;
  errors.Add( filename );
  errors.Add( line );
  wxString msgFix = msg;
  msgFix.Replace( _T( "\t" ), _T( "    " ) );
  errors.Add( msgFix );
  Logger::level lv = ( lt == cltError ) ? Logger::error : ( lt == cltWarning ) ? Logger::warning : Logger::info;
  static wxDateTime lastAutofitTime = wxDateTime( ( time_t )0 );
  if( lastAutofitTime < ( wxDateTime::Now() - wxTimeSpan::Seconds( 3 ) ) ) {
    lastAutofitTime = wxDateTime::Now();
    m_pListLog->Append( errors, lv, 2 );
  } else {
    m_pListLog->Append( errors, lv );
  }
  m_Errors.AddError( lt, prj, filename, line.IsEmpty() ? 0 : atoi( wxSafeConvertWX2MB( line ) ), msg );
}

void CompilerGCC::LogMessage( const wxString& message, CompilerLineType lt, LogTarget log, bool forceErrorColour, bool isTitle, bool updateProgress ) {
  wxString msgInput, msg;
  if( message.StartsWith( COMPILER_SIMPLE_LOG, &msg ) ) {
    msgInput = msg;
  } else {
    msgInput = message;
  }
  if( msgInput.StartsWith( COMPILER_NOTE_ID_LOG, &msg ) ) {
    LogWarningOrError( lt, 0, wxEmptyString, wxEmptyString, msg );
  } else if( msgInput.StartsWith( COMPILER_WARNING_ID_LOG, &msg ) ) {
    if( lt != cltError ) {
      lt = cltWarning;
    }
    LogWarningOrError( lt, nullptr, wxEmptyString, wxEmptyString, msg );
  } else if( msgInput.StartsWith( COMPILER_ERROR_ID_LOG, &msg ) ) {
    if( lt != cltError ) {
      lt = cltWarning;
    }
    LogWarningOrError( cltError, nullptr, wxEmptyString, wxEmptyString, msg );
  } else {
    msg = msgInput;
  }
  if( log & ltFile ) {
    if( forceErrorColour ) {
      m_BuildLogContents << _T( "<font color=\"#a00000\">" );
    } else if( lt == cltError ) {
      m_BuildLogContents << _T( "<font color=\"#ff0000\">" );
    } else if( lt == cltWarning ) {
      m_BuildLogContents << _T( "<font color=\"#0000ff\">" );
    }
    if( isTitle ) {
      m_BuildLogContents << _T( "<b>" );
    }
    wxString sQuoted( msg );
    wxString sGA = wxString::FromUTF8( "\x60" );
    wxString sAA = wxString::FromUTF8( "\xC2\xB4" );
    sQuoted.Replace( sGA, _T( "\"" ), true );
    sQuoted.Replace( sAA, _T( "\"" ), true );
    sQuoted.Replace( _T( "&" ), _T( "&amp;" ), true );
    sQuoted.Replace( _T( "<" ), _T( "&lt;" ), true );
    sQuoted.Replace( _T( ">" ), _T( "&gt;" ), true );
    m_BuildLogContents << sQuoted;
    if( isTitle ) {
      m_BuildLogContents << _T( "</b>" );
    }
    if( lt == cltWarning || lt == cltError || forceErrorColour ) {
      m_BuildLogContents << _T( "</font>" );
    }
    m_BuildLogContents << _T( "<br />\n" );
  }
  if( log & ltMessages ) {
    Logger::level lv = isTitle ? Logger::caption : Logger::info;
    if( forceErrorColour ) {
      lv = Logger::critical;
    } else if( lt == cltError ) {
      lv = Logger::error;
    } else if( lt == cltWarning ) {
      lv = Logger::warning;
    }
    wxString progressMsg;
    if( updateProgress && m_CurrentProgress < m_MaxProgress ) {
      ++m_CurrentProgress;
      float p = ( float )( m_CurrentProgress * 100.0f ) / ( float )m_MaxProgress;
      progressMsg.Printf( _T( "[%5.1f%%] " ), p ); //输出编译进度百分比
      if( m_pLog->progress ) {
        m_pLog->progress->SetRange( m_MaxProgress );
        m_pLog->progress->SetValue( m_CurrentProgress );
      }
    }
    Manager::Get()->GetLogManager()->Log( progressMsg + msg, m_PageIndex, lv );
    Manager::Get()->GetLogManager()->LogInf( progressMsg + msg );
  }
}

void CompilerGCC::InitBuildLog( bool workspaceBuild ) {
  wxString title;
  wxString basepath;
  wxString basename;
  if( !workspaceBuild && m_pProject ) {
    title = m_pProject->GetTitle();
    basepath = m_pProject->GetBasePath();
    basename = wxFileName( m_pProject->GetFilename() ).GetName();
  } else if( workspaceBuild ) {
    M_Code_Workspace* wksp = Manager::Get()->GetProjectManager()->GetWorkspace();
    title = wksp->GetTitle();
    basepath = wxFileName( wksp->GetFilename() ).GetPath( wxPATH_GET_VOLUME | wxPATH_GET_SEPARATOR );
    basename = wxFileName( wksp->GetFilename() ).GetName();
  }
  if( basename.IsEmpty() ) {
    basename = _T( "unnamed" );
  }
  m_BuildStartTime = wxDateTime::Now();
  m_BuildLogTitle = title + _T( " 编译日志" );
  m_BuildLogFilename = basepath;
  m_BuildLogFilename << basename << _T( "_build_log.html" );
  m_BuildLogContents.Clear();
  m_MaxProgress = 0;
  m_CurrentProgress = 0;
}

void CompilerGCC::SaveBuildLog() {
  if( !Manager::Get()->GetConfigManager( _T( "compiler" ) )->ReadBool( _T( "/save_html_build_log" ), false ) ) {
    return;
  }
  if( m_BuildLogFilename.IsEmpty() ) {
    return;
  }
  wxFile f( m_BuildLogFilename, wxFile::write );
  if( !f.IsOpened() ) {
    return;
  }
  f.Write( _T( "<html>\n" ) );
  f.Write( _T( "<head>\n" ) );
  f.Write( _T( "<title>" ) + m_BuildLogTitle + _T( "</title>\n" ) );
  f.Write( _T( "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=utf-8\" />" ) );
  f.Write( _T( "</head>\n" ) );
  f.Write( _T( "<body>\n" ) );
  f.Write( _T( "<tt>\n" ) );
  f.Write( _T( "Build started on: " ) );
  f.Write( _T( "<u>" ) );
  f.Write( m_BuildStartTime.Format( _T( "%d-%m-%Y at %H:%M.%S" ) ) );
  f.Write( _T( "</u><br />\n" ) );
  f.Write( _T( "Build ended on: " ) );
  f.Write( _T( "<u>" ) );
  f.Write( wxDateTime::Now().Format( _T( "%d-%m-%Y at %H:%M.%S" ) ) );
  f.Write( _T( "</u><p />\n" ) );
  f.Write( m_BuildLogContents );
  f.Write( _T( "</tt>\n" ) );
  f.Write( _T( "</body>\n" ) );
  f.Write( _T( "</html>\n" ) );
  Manager::Get()->GetLogManager()->Log( _T( "生成日志另存为: " ), m_PageIndex );
  wxString tempBuildLogFilename = m_BuildLogFilename;
  tempBuildLogFilename.Replace( _T( "\\" ), _T( "/" ) );
  wxURI tmpFilename = tempBuildLogFilename;
  Manager::Get()->GetLogManager()->Log( F( _T( "file://%s" ), tmpFilename.BuildURI().wx_str() ), m_PageIndex, Logger::warning );
}

void CompilerGCC::OnJobEnd( size_t procIndex, int exitCode ) {
  m_timerIdleWakeUp.Stop();
  m_CompilerProcessList.at( procIndex ).PID = 0;
  m_CompilerProcessList.at( procIndex ).pProcess = 0;
  if( m_LastExitCode == 0 || exitCode != 0 ) {
    m_LastExitCode = exitCode;
  }
  bool success( exitCode == 0 );
  Compiler* compiler = CompilerFactory::GetCompiler( m_CompilerId );
  if( compiler ) {
    success = ( exitCode >= 0 ) && ( exitCode <= compiler->GetSwitches().statusSuccess );
  }
  wxString oFile = UnixFilename( m_CompilerProcessList.at( procIndex ).OutputFile );
  Manager::Get()->GetMacrosManager()->ReplaceMacros( oFile );
  if( success && !oFile.IsEmpty() ) {
    float displaySize;
    wxString units;
    wxLogNull silence;
    wxFFile f( oFile.wx_str(), _T( "r" ) );
    if( f.IsOpened() ) {
      size_t size = f.Length();
      f.Close();
      if( size < 1024 ) {
        displaySize = ( float )size;
        units = _T( "bytes" );
      } else if( size < 1048576 ) {
        displaySize = ( float )size / 1024.0f;
        units = _T( "KB" );
      } else {
        displaySize = ( float )size / 1048576.0f;
        units = _T( "MB" );
      }
      wxString msg;
      msg.Printf( _T( "\n\n\t\t输出文件 %s \n\t\t文件尺寸 %.2f %s" ), oFile.wx_str(), displaySize, units.wx_str() );
      LogMessage( msg, cltNormal );
    }
  }
  if( success ) {
    m_LastExitCode = 0;
  }
  if( m_CommandQueue.GetCount() != 0 && success ) {
    DoRunQueue();
  } else {
    if( success ) {
      if( IsProcessRunning() ) {
        DoRunQueue();
        return;
      }
      while( 1 ) {
        BuildStateManagement();
        if( m_CommandQueue.GetCount() ) {
          DoRunQueue();
          return;
        }
        if( m_BuildState == bsNone && m_NextBuildState == bsNone ) {
          break;
        }
      }
    }
    m_CommandQueue.Clear();
    ResetBuildState();
    while( !m_BuildJobTargetsList.empty() ) {
      m_BuildJobTargetsList.pop();
    }
    wxString msg = F( _T( "\t\t进程已终止状态为  %d (%s)" ), exitCode, GetMinSecStr().wx_str() );
    if( m_LastExitCode == exitCode ) {
      LogMessage( msg, success ? cltWarning : cltError, ltAll, !success );
    }
    if( !m_CommandQueue.LastCommandWasRun() ) {
      if( !IsProcessRunning() ) {
        msg = F( _T( "\t\t%s (%s)" ), GetErrWarnStr().wx_str(), GetMinSecStr().wx_str() );
        success = ( m_LastExitCode >= 0 ) && ( m_LastExitCode <= compiler->GetSwitches().statusSuccess );
        LogMessage( msg, success ? cltWarning : cltError, ltAll, !success );
        LogWarningOrError( cltNormal, 0, wxEmptyString, wxEmptyString, F( _T( "=== 编译%s: %s ===" ),
                           wxString( m_LastExitCode == 0 ? _T( "完成" ) : _T( "失败" ) ).wx_str(), msg.wx_str() ) );
        m_pListLog->AutoFitColumns( 2 );
        SaveBuildLog();
      }
      if( !Manager::IsBatchBuild() && m_pLog->progress ) {
        m_pLog->progress->SetValue( 0 );
      }
    } else
    { m_LastExitCode = 0; }
    Manager::Get()->GetLogManager()->Log( _T( " " ), m_PageIndex );
    NotifyJobDone();
    if( !Manager::IsBatchBuild() && m_Errors.GetCount( cltError ) ) {
      if( Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_show_build_errors" ), true ) ) {
        CodeBlocksLogEvent evtShow( cbEVT_SHOW_LOG_MANAGER );
        Manager::Get()->ProcessEvent( evtShow );
      }
      CodeBlocksLogEvent evtSwitch( cbEVT_SWITCH_TO_LOG_WINDOW, m_pListLog );
      Manager::Get()->ProcessEvent( evtSwitch );
      if( Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_focus_build_errors" ), true ) ) {
        m_pListLog->FocusError( m_Errors.GetFirstError() );
      }
    } else {
      if( m_RunAfterCompile ) {
        m_RunAfterCompile = false;
        if( Run() == 0 ) {
          DoRunQueue();
        }
      } else if( !Manager::IsBatchBuild() ) {
        CodeBlocksLogEvent evtGetActive( cbEVT_GET_ACTIVE_LOG_WINDOW );
        Manager::Get()->ProcessEvent( evtGetActive );
        if( evtGetActive.logger == m_pLog ) {
          if( m_Errors.GetCount( cltWarning ) &&
              Manager::Get()->GetConfigManager( _T( "message_manager" ) )->ReadBool( _T( "/auto_show_build_warnings" ), true ) ) {
            CodeBlocksLogEvent evtShow( cbEVT_SHOW_LOG_MANAGER );
            Manager::Get()->ProcessEvent( evtShow );
            CodeBlocksLogEvent evtSwitch( cbEVT_SWITCH_TO_LOG_WINDOW, m_pListLog );
            Manager::Get()->ProcessEvent( evtSwitch );
          } else {
            CodeBlocksLogEvent evtShow( cbEVT_HIDE_LOG_MANAGER );
            Manager::Get()->ProcessEvent( evtShow );
          }
        }
      }
    }
    m_RunAfterCompile = false;
    M_Code_Editor* ed = Manager::Get()->GetEditorManager()->GetBuiltinEditor( Manager::Get()->GetEditorManager()->GetActiveEditor() );
    if( ed ) {
      ed->GetControl()->SetFocus();
    }
  }
}

void CompilerGCC::NotifyJobDone( bool showNothingToBeDone ) {
  if( !m_LastBuildStep ) {
    return;
  }
  m_BuildJob = bjIdle;
  if( showNothingToBeDone && m_Errors.GetCount( cltError ) == 0 ) {
    LogMessage( m_Clean ? _T( "完成.\n" ) : _T( "无需执行任何操作（所有项目都是最新的）.\n" ) );
    CodeBlocksLogEvent evtShow( cbEVT_HIDE_LOG_MANAGER );
    Manager::Get()->ProcessEvent( evtShow );
  }
  if( !IsProcessRunning() ) {
    ProjectManager *manager = Manager::Get()->GetProjectManager();
    if( manager->GetIsRunning() == this ) {
      manager->SetIsRunning( NULL );
    }
    CodeBlocksEvent evt( cbEVT_COMPILER_FINISHED, 0, m_pProject, 0, this );
    evt.SetInt( m_LastExitCode );
    Manager::Get()->ProcessEvent( evt );
    m_LastExitCode = 0;
  }
}

wxString CompilerGCC::GetErrWarnStr() {
  return F( _T( "%u 错误, %u 警告" ), m_Errors.GetCount( cltError ), m_Errors.GetCount( cltWarning ) );
}

wxString CompilerGCC::GetMinSecStr() {
  long int elapsed = ( wxGetLocalTimeMillis() - m_StartTime ).ToLong() / 1000;
  int mins = elapsed / 60;
  int secs = ( elapsed % 60 );
  return F( _T( "%d 分, %d 秒" ), mins, secs );
}
